In this project, you will extend your solution to project 5 to incorporate new features into the whiteboard. Most of these features will be implemented with reflection. Please start early, even though this project is less complicated than the previous one.
Here are the new files for this project.
Submit this project by cd'ing into the top level directory and running
In project 5, you wrote code so that when the whiteboard server called publish(), those calls would time out after 5 seconds. This seems like a pretty handy feature to have in general, and we might like to apply it in many more situations. For the first extension to project 5, you will refactor your timeout code into a new class that uses dynamic proxies (which we talked about in class; see slides 97-104 from lecture) to allow us to add timeouts to nearly any method calls to any class.
You must implement the following class, which you should put in the server/ directory:
public class TimedObject {
/** Don't change the signature of this method */
public static Object createTimedObject(Object source,
Tracker tracker,
long ms) {
/*** IMPLEMENT ME ***/
}
}
public interface Tracker {
public void success();
public void failure();
public void exception(Throwable e);
}
Here is an example of applying this proxy to a Subscriber object:
Subscriber s = ... // Some subscriber
Subscriber timed_s =
(Subscriber) TimedObject.createTimedObject(s, myTracker, 5000);
timed_s.publish();
The TimedObject class takes as a parameter an object source. It returns a new object result that implements exactly the same set of interfaces as source. Each time a method void result.foo() is invoked, it returns after creating a new thread that will invoke source.foo(). The tracker will be called with the three different possible results depending on what happens with the invocation of source.foo() in the new thread:
After you've written this class, change your RemoteBoard to use TimedObject to invoke calls to publish().
We will test TimedObject independently from your project. We will also test your project with our implementation of TimedObject swapped in. So while you can add other methods to TimedObject and you can even make TimedObject extend other classes or implement other interfaces, be sure to leave the createTimedObject method as-is.
Suppose that you've spent hours drawing your own Mona Lisa on the whiteboard, and then the server crashes. Oops, you've just lost all that work! Wouldn't it be nice to be able to save the state of the Whiteboard into a file on the client side, so that you could restore it if you needed to? That's what you'll do for this part of the project.
In order for this to work, you'll need permission to access the files on the disk, which means you'll need to modify the java.policy file appropriately. We'll leave it up to you to figure out exactly how to do this, but you must not grant remote code any additional permissions. Submit your client's new java.policy file along with the rest of the project.
All of the Shapes used in the project are serializable, so we could just use Java's standard mechanism to save them. But if we do that, then the file would be in some binary format that would not be human-readable. We'd like to make a file that a user could tweak a little by hand. (Plus, just using Serializable would be too easy!)
We've provided you with a SaveButton class that will take care of the GUI interface to saving the whiteboard. (Put this file in the client/ directory.) You can add methods to this class, but don't change the signature of the writeShapes() method, because that is what we will call to test your code.
In order for you to use SaveTool, you should modify Whiteboard.java to add the save tool to the tool bar:
tools.add(new SaveButton("Save", w));
where w is the WhiteboardState. Clicking on save and entering a file name should generate a file in the following format. Each line should look like:
ShapeClass name1=val1 name2=val2 ... namek=valkwhere name1...namek are the public fields of ShapeClass. For example the file might look like
Circle radius=10 x=5 y=25 Rect width=20 height=30 x=55 y=27 CS433001Text text="Hello" x=30 y=30
Your SaveTool should work for any Shape, even ones that aren't written yet, so you'll need to use reflection to get the class names and fields of the shapes. Use one space between each element on each line, and don't put spaces before or after the equals. When someone clicks on Save, you should get the list of shapes from the server and iterate through them. For each shape, use reflection to get the list of public fields for the shape and output the values of the fields to the file. You only have to handle fields that are either Strings or ints. You can assume that Strings don't have any characters that need to be escaped, but you should put your strings in quotes to allow for spaces.
You should simply ignore fields of other types as well as private fields. You need to output all of the public int and String fields of the Shape, including ones that are inherited from parent classes (e.g., AbstractShape). (This is easy to do if you pick the right method to get the set of fields.) You don't need to list the fields in any particular order, though, and in the next part (below) you shouldn't rely on them being in any particular order.
If you try to create a file that you can't create, bring up an alert box using MessageBox saying Unable to create <filename>.
We'll test this part of the project by connecting your client with our own server, adding a bunch of shapes to the server, and then saving the results and comparing them to what we expect.
Naturally, once we're able to save the state of the whiteboard, it would also be nice to load it. We've provided you with a LoadTool class that will take care of the GUI interface; you should add it to your whiteboard as above. You can add methods to this class, but don't change the signature of the readShapes() method, because that is what we will call to test your code.
The load tool should take a file in the format specified above. For each Shape listed in the file, it should use reflection to create a new instance of that shape using the nullary constructor. (If you didn't add a nullary constructor to your CS4330xx class, this would be a good time to do so.) Then for each public field listed, the load tool should use reflection to set the value of that field.
Be sure to handle quotes around String fields. In particular, spaces and equals signs may appear between quotes. You can ignore escaped characters, though.
The save and load tools are really designed to be used just before you stop the whiteboard and just after you start it again. In particular, a save followed by a load (without stopping the whiteboard server) will result in two copies of each shape being on the server.
You might be wondering why we don't just use the non-nullary constructors. That's because it's hard to know which parameters of the constructors correspond to which fields. As a side effect, since the id and version fields of a shape are not public, those will not be saved and restored, but rather they will be fresh each time you load in the whiteboard. The color field is also not public, and due to a quirk in the AbstractShape class, the loaded shapes will be drawn in black.
Your load tool should be tolerant of mistakes in the file. In particular, if you can't parse the file or if there are any mistakes in the file (e.g., a field name is mistyped or there's an entry for a private field), your whiteboard should not crash because of it. Instead, it should bring up an alert box with a warning Can't parse <filename>. Once you bring up an alert box, it doesn't matter what state you've left the whiteboard in (e.g., you may have partially loaded the file) as long as the whiteboard doesn't crash. In other words, after trying to load a bad file, you should still be able to add shapes, respond to publish events from the server, etc.
Your load tool need not bring up an alert box with a warning for files that use more than one space between fields, files that add extra spaces before or after equals, or files that use escaped characters in strings. We won't test your code with those cases. In other words, if you write a fancier parser that handles arbitrary whitespace and escaped characters in quotes, that's fine, and if you don't, that's fine as well.
If you try to read a file that doesn't exist or in inaccessible, bring up an alert box with a warning Unable to read <filename>.
We will re-run all of the project 5 test cases on your project 6, and this will count for 25% of your project 6 grade. If your project 5 was fully correct, then great! You've already done part of project 6. If you lost points on project 5, you need to fix those errors.
We will get you project 5 grades as soon as possible. Don't let not having project 5 grades stand in the way of doing parts 1-3.
Think of this as a taste of the real world. If you don't do something quite right, you don't get to skip it and go on to something else.
You can earn up to 10 extra points (and bragging rights!) for the project by adding some cool, innovative new features to the whiteboard. We won't suggest any particular features for you to add; it's up to you to be creative. Your extra credit points will be determined by the quality, not the quantity of the extra features and code you add. You will get no points for making small improvements (even if you make a lot of them), such as adding an oval tool or a color picker. You will get no points if your basic project 6 code doesn't work very well, so work on the rest of this project first. You will get no points if your extra credit project doesn't work, even if it has an interesting design. You will get the most points for features that have interesting designs or interesting implications for distributed vs. local whiteboards.
Do not submit any part of your extra credit changes with your regular project submission. Instead, you should work on the extra credit in a completely separate directory, and submit it with
For the extra credit part only, you are allowed to change the client and server code and interfaces arbitrarily. Remember, don't make these changes to the regular project code.
For the extra credit, you should submit a complete whiteboard client and server, i.e., include all necessary files even if you didn't change them. Your extra credit submission must also include a README file that explains your improvements. Your README file should include a discussion of your coding changes, possibly explained in terms of the design patterns we saw during the semester.
Grades for the extra credit will be determined by the professor, and will be assigned based on the following scale:
The extra credit is due Tuesday, May 11 at midnight, with no late extensions.