| Revision History | ||
|---|---|---|
| Revision 1.0 | 2004.01.16 | JC4 |
| Initial drafting of the lab, importing of parts of tools section from previous semesters. | ||
Table of Contents
![]() | Important |
|---|---|
Part 1 is due on WebCT before 2004.01.30 08:00:00 (A.M.)! Part 2 is due on WebCT before 2004.02.06 08:00:00 (A.M.)! Part 3 is due on WebCT before 2004.02.13 08:00:00 (A.M.)! ***DO NOT WAIT UNTIL THE LAST MINUTE TO SUBMIT YOUR ASSIGNMENT!!!*** | |
Everyone is familiar with the basic paint programs that are typically bundled with their favorite/least-favorite OS. NetPaint is similar to those programs, but with the (obvious) added twist that it is networked.
In the past, you have had experience using Swing components to develop Graphical User Interfaces (GUIs). This lab will expand these skills and give you the ability to create customized graphics by using the Graphics[2D] class. By the end of the lab, you will have created a simple paint program using these new GUI creation skills.
In Java, manipulation of Graphics objects is not a particularly difficult task. Since the Graphics class is abstract, you cannot directly create one by calling its constructor. You must instead obtain them from previously defined Graphics objects or by calling the getGraphics() method on a Component(java.awt.Component) or any of its subclasses. Before you call the getGraphics() method on a component, it must be displayable (i.e. the [J]Frame to which the component is added must be visible - [J]Frame.setVisible(true)). Once you have a Graphics object, you can begin to manipulate it. Graphics objects have methods used to draw basic shapes, such as characters, strings, polygons, but anything more complex will require you to combine shapes to achieve the desired output. Look at the Java API (java.awt.Graphics) for the exact commands necessary to do this. Unfortunately, accessing graphics in this manner causes some problems (mainly every time the window is changed - minimized, resized, overlapped by another window, etc... - the graphic you just made will be erased). To correct this problem, you need to override the paintComponent() method in the Component whose Graphic you wish to access. This method is called every time the window needs to be redrawn, so by doing all of your Graphics changes in this method you will fix the aforementioned problem of losing your Graphic.
The Graphics2D class in Java allows for more flexibility when rendering Graphical Objects (Graphics objects can be cast to Graphics2D objects). Check out this Graphics2D tutorial for more information on this subject. Other sections of the Java tutorial may be of additional assistance.
We've provided a simple Graphics example, BasicGraphics.java, that shows you what can be done with basic calls to a Graphics object.
Although you will not be required to animate anything in this lab, it might be helpful to look over this section for help in future labs (wink, wink).
Making something animated is as simple as creating a Timer that periodically repaints the components to be animated.Java implements a Timer(javax.swing.Timer) to be used in animations. A Timer requires an actionListener and a delay. After you call start() on the Timer, the actionListener will be called every "delay" milliseconds. The actionListener will make the changes necessary to create the next frame and then call repaint on the component, which actually renders it.
Drawing straight to the screen is a bad idea - it looks really bad, since the individual objects will be drawn to the screen in a delayed fashion, instead of all at once. To avoid this problem, a technique called double buffering comes into play. Double buffering is a method that renders the next image to be displayed in an off-screen buffer and then swaps it with the current image (do a search on Google for double buffering in Java for some good examples). However, thanks to the helpful people at sun, if you use Swing Components, double buffering is done for you.
We've also provided you with an Animation example, SimpleAnimation.java.
Since its nearly impossible to get the complete desired graphical effects and animations by simply using Graphics2D, images often need to come into play. While there are numerous image subclasses, and practically infinite combination of ways to load them, for the sake of simplicity and speed its recommended to use BufferedImages. A BufferedImage can be drawn from a Graphics(2D) object's drawImage(...) methods, the tricky part is efficiently loading an image.
To load a BufferedImage from a file, the class you should be interested in is javax.imageio.ImageIO and more specifically the read(URL ...) method You'll notice the method is static, so there is no need to init the class and can simply be used as such below:
BufferedImage toLoad = javax.imageio.ImageIO.read( getClass().getResource("myImage.png"));
The getClass().getResource(...) method calls are simply a way to return a URL from path, referenced from the current classes location. The read(...) method used as such above, well return a fully loaded BufferedImage, meaning that you will NOT have worry about using something such as the MediaTracker to ensure the image is ready to be drawn.
If after using BufferedImages, you find your frame-rate is still being hindered, you can attempt to pass off some of the work to the graphics card by making the images hardware accelerated.
import java.awt.*;
import java.awt.image.*;
public class ImageLoader
{
final GraphicsConfiguration gc;
public ImageLoader(GraphicsConfiguration gc)
{
if(gc==null)
{
gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
this.gc = gc;
}
public BufferedImage loadImage(String resource)
{
try
{
BufferedImage src = javax.imageio.ImageIO.read(getClass().getResource(resource));
BufferedImage dst = gc.createCompatibleImage(src.getWidth(),src.getHeight(),src.getColorModel().getTransparency());
Graphics2D g2d = dst.createGraphics();
g2d.setComposite(AlphaComposite.Src);
g2d.drawImage(src,0,0,null);
g2d.dispose();
return dst;
}
catch(java.io.IOException e)
{
return null;
}
}
}
With a class similar to above, using the loadImage(...) method will return a fully accelerated BufferedImage ready to be rendered. If you look more carefully at the loadImage(...) method you should already see concepts that are/will be familiar to you. A BufferedImage has Graphics2D object that can be directly drawn upon just like any other rendering surface. The loadImage(...) method simply loads an image through the method explained above, then creates another "blank" BufferedImage with acceleration, and then proceeds to draw the first image onto the second. If desired, you could add further code below the g2d.drawImage(...) to draw lines/text/etc that would be permanently stored upon the image.
Threads allow multiple concurrent paths of execution. In Java, there are different ways to create a new thread:
When running on a machine with multiple processors, multiple threads can actually run at the same time. Under normal circumstances, on uniprocessor machines, the threads are time-shared, rapidly switching back and forth, simulating concurrent execution.
A class which is going to run as its own thread needs to have a method called run declared as public and void, with no arguments. When the thread is started with start(), run will be called and will run separately from the rest of the program.
We've provided an extremely simple threaded example called ThreadExample.java.
If you try this out, you'll see that "String One" and "String Two" will be printed on the console in some random order, since both threads are running at once and sleeping random amounts between messages.
Threads are important in a wide variety of applications. One of their most important applications is networking. Traditional networking makes heavy use of threads, because traditional networking uses blocking I/O. In other words, when you call a function, it doesn't return until it's finished. When you wait for a client to connect, you can't continue until you receive a connection. When you want to read from a connection, you can't continue until you've filled your buffer. When you write to a socket, you can't continue until all of the data has been written. Without threads, it would be nearly impossible to do anything useful since you would always be waiting on one client, unable to handle all the others.
This is where threads come in. On the server end, each client will get its own thread. That way, the blocking operations each block in their own threads, without affecting any of the other threads. On the client end, each client-server and client-client connection gets its own thread. In addition, the user interface gets its own thread, so that the user can interact with the program even when all of the network connections are blocking.
In Java, networking is fairly simple. On the server end, the server creates a ServerSocket. This socket repeatedly accept()s, getting a Socket back from each call and spawning a thread to handle each new Socket.
On the client end, it's even easier. Just create a socket and connect() it to a listening server.
The most useful Socket functions are getInputStream() and getOutputStream(). By wrapping these streams in other IO classes (such as PrintWriter and BufferedReader), you can do all of the reading and writing you'll ever need.
We've provided you with a simple client and server pair, to see what a threaded networking program looks like. These are the ClientExample.java and ServerExample.java.
Server
Client
![]() | Note |
|---|---|
Connection to the server must be threaded - otherwise, interaction with the UI will not be possible. | |
Here is a link to Java's Networking Tutorial: http://java.sun.com/docs/books/tutorial/networking/index.html
NetPaint is a networked paint program where two users may interact with a single drawing canvas. Here both users can see the image and both can interact with it (e.g. drawing new parts of the image).
Before programming you team should define a protocol that will be used throughout the project. This will keep the confusion to a minimum. There are many, many different ways to define a protocol, but here is a simple one that should give you an idea of how to do it. Also for the most part, your server will be broadcasting all of the messages directly to all of the clients without any modification.
-------------- Initial Sign-on ---------------
> HELO: [username]
------------ Send a chat message ------------
> CHAT: [username]:TEXT
--------------- Draw a line -----------------
> LINE: [username]:StartX,StartY:EndX,EndY:RGB Color
------------- Draw a Rectangle --------------
> RECT: [username]:StartX,StartY:Height:Width:RGB Color:Fill (Y/N)
-------------- Undo previous step -----------
> UNDO: [username]
----------------- Sign-off ------------------
> QUIT: [username]
Also note that by using the above protocol, you need to prevent users
from using a ':' in their username. This protocol was done as an example,
and does not represent the best solution or a complete one.
Your server must support at least 5 simultaneous clients, which means that the server must be multi-threaded. Also, the server must prevent two users from specifying the same username. All of the networking in this lab must be handled by using TCP Sockets, which are described very well in Java's networking tutorial (linked in section 2.3).
Special Note: Your team must determine a method for handling the "ordering of events." This means that it is your choice as to how "simultaneous events" (occurring on the two systems) are handled. You may select an "absolute ordering" of events which prevents inconsistencies between what each user sees **OR** you may select to use "receive ordering" where each system processes the event in the order it is received this is probably the easiest to implement but may result in minor inconsistencies between what each of the users sees.
NetPaint should be able to do the following:
![]() | Note |
|---|---|
You are only required to support the use of images that are available via the internet (See javax.swing.ImageIcon(java.net.URL)) and will require you to transfer only the text of the URL (e.g. "http://www.cc.gatech.edu/classes/AY2004/cs2335_spring/header.gif") between the two systems and let both independently download it from the internet. EXTRA CREDIT OPTION: For extra credit your team may support the use of an image that located on a local disk of one of the computers, here it is YOUR responsibility for transferring the image to the other computer. Do not assume that the other system has any other way of accessing the image (e.g. it's not on the internet or a networked drive). | |
TIPS: Your team may find it helpful to visit the Graphics2D tutorial at http://java.sun.com/docs/books/tutorial/2d/display/index.html
EXTRA CREDIT OPTION: Provide support for using the most common emoticons (e.g. the user's typing :-P in the text input area will be translated into the appropriate icon when displayed in the received test area.
Here is an example of how your program could look:
+-------------------------------------------------------------------------------------+
| File | Options | Help |
+-------------------------------------------------------------------------------------+
| | |
| | Rectangle |
| | |
| |-------------|
| | |
| | Circle |
| | |
| |-------------|
| Graphics Display Panel | |
| | Text |
| | |
| |-------------|
| | |
| | Polygons |
| | |
| |-------------|
| | |
| | etc |
| | . |
|-----------------------------------------------------------------------| . |
| Option Bar - Change Color, Font Size, Font Type, etc... | . |
+-----------------------------------------------------------------------+-------------+
| |
| Received Text Area |
| |
+-------------------------------------------------------------------------------------+
| Text Input Area | Send Text Button |
+-------------------------------------------------------------------------------------+
Suggested Package Structure:
- lab4
- server
* ServerMain (ex: lab4.server.ServerMain)
* ServerThread
- client
* ClientMain
- networking
* ClientNetworking
- model
* RenderableObjectList
* RenderableImage
* RenderableShape
* RenderableText
* RenderableObject
* ClientEventListener
- gui
* GraphicsPanel
Legend:
"*" - Denotes a class
"-" - Denotes a package
![]() | Note |
|---|---|
Your team is free to come up with an entirely new design, however partial-credit opportunities will be limited if you use another design. | |
![]() | Note |
|---|---|
We will not be checking PMD or Checkstyle for Part 1 or 2, but we STRONGLY recommend you keep both tools in mind when programming. | |
For the first part of the lab, you need to fully implement the server component of your system. You should be able to ssh into your sever and perform any commands, which should then be properly broadcast to all other connected clients. Although your server must support all message types, you may still change them for the further deliverables if necessary.
One of your group members must turn in the following:
Please submit an archive of all these files in one of the following formats: zip, tar, jar, or gzip.
For the second part of the project, you must submit everything from Part 1 and the basic client, which needs to support the following features:
One of your group members must turn in the following:
Please submit an archive of all these files in one of the following formats: zip, tar, jar, or gzip.
For the final part of the lab, you must submit everything from Part 1 and Part 2, but now everything must be fully functional.
One of your group members must turn in the following:
Please submit an archive of all these files in one of the following formats: zip, tar, jar, or gzip.
You may receive up to 20 points of extra credit for Part 3 of this lab. You may attempt the following for extra credit (and their point values):
![]() | Important |
|---|---|
Unless arranged in advance, your TA will be grading this assignment on the RedHat systems available in the States Lab and will be using the Java tools provided in the "~cs2335/" directory. It is YOUR responsibility to verify that your program will work on these systems prior to submitting it. | |