In this assignment you will continue to build the Pizzera API and start on an application
to use it.
Remember your Lessons Learned
Please do write up notes in your Lessons Learned from the prior version and any that you learn while working on this one.
You may want to add/adjust them as you receive feedback from the prior version's evaluation.
Here are some areas for you to consider:
- How does your solution follow the terms discussed in
Design Pattern
- Are your classes independent and self-contained? In other words, do they demonstrate
proper encapsulation, separation of concerns, low coupling, and high cohesion?
- How does your solution use packages?
- How did you analyze the data and design your datafile structure?
- What are the advantages and what are the challenges of processing the data file as you read it rather reading it into a buffer to process later or read it in multiple passes?
- What are the advantages and what are the challenges of using the generic OptionSet/Option classes rather than coding a Pizza class, or a VegChoices class, ...?
- What were the advantages/challenges of using arrays rather than a ArrayList class?
- How did using an ArrayList and LinkedHashMap change the design of your system?
- What aspects of your design presented a challenge and how did your solution address them?
Note: failing to follow the design principles in the best way possible is also an
example of a "lesson learned", so do include any thoughts about "If only I had done XYZ, it would have been better.", and include why.
Part A
Requirements
This project needs more work!
You need to implement a better way to create Pizzerias, rather than just hard-coding
them. To do this, you will design a flat-file structure and write the code to parse this file
to configure Pizzerias.
Note: A "flat-file" is a text file of data that does not follow a pre-defined format.
And now that your API holds all Pizzerias in a single instance (with static, and
perhaps as a singleton) you need to ensure
that it functions correctly when there are multiple users. You will need to make the system(s)
using your API able to scale when multiple users add Pizzerias, update the same Pizzeria,
delete an OptionSet in the same Pizzeria, update the same Option value, ...
all at the same time.
Plan of Attack
To ensure that your application can handle concurrency, you have to add new functionality in new
classes without modifying our existing code (too much). This means that ProxyPizzeria combined
with interfaces is a
component of a sort that we want to keep intact. Your model package contains the Model,
which is made up of the PizzaConfig, OptionSet, and Option classes,
plus the Pizzeria name (wherever you put that) represents
a single Pizzera. The PizzaConfig class controls all access to the configuration settings.
Your ProxyPizzeria represents all configured Pizzerias and controls access
to the internal data (PizzaConfigs) stored in the Model.
Your ProxyPizzeria setup a scalable structure, by adding a LinkedHashMap to store each
Pizzeria and some simple API's to modify it. (We can always add more methods in
interfaces at any time so we are not going to worry about that for now).
Step 1: Read Pizzeria Configuratons from a text file (flat file)
You will design a flat file, meaning there is no widely used markup to give the
data structure. You will design it yourself to follow the requirements outlined below.
-
Create a text file that contains all the properties and value that are described for the
requirements so far. The format of the file is to be created by you. TA's or Instructor will NOT help you decide on the structure of this file.
-
Your file-format design should use the following criteria:
-
You can make this engineer friendly or user friendly.
-
When reading file you can only read the file once to populate the Model (in other words, no
multi-pass file reading algorithms).
-
When reading file you cannot use a buffer or any data structure a list to save the
bulk data temporarily in memory.
- In other words, read part of the file and use it create the objects and update them as more
data is read in.
-
Do not create or use a random-access file or XML file.
-
After designing the input file, create a package named io and put your code there that
will read the data and add the result to the Model.
Wherever you put the code, call the method buildPizzaConfig().
This will be accessed via your API through the
configurePizzeria() method defined in the CreatePizzeria interface,
the one you did not fully code:
void configurePizzeria(String filename);
Note 1: Do not use the createPizzeria() method your coded the prior version that is passed in
PizzaConfig(s) - this was only added to
aid in your testing in that version and give you the ability to create PizzaCongis before
implementing the file-input feature in this version.
Note 2: Do NOT put anything related to files in the model package directly; the code belongs
in your io module.
Doing this will help separate code organization according to class responsibilities; think
"seperation of concerns".
-
When creating the class/method(s) for reading data from a text file you should follow these
guidelines:
-
You must use FileReader and BufferedReader classes for working with text files,
as covered in the posted ReadSource example.
-
Put the class in a package called io.
-
In the io module, ONLY use methods from PizzaConfig class and do not use any methods from
OptionSet and Option class.
-
Make sure there is no reference to code related to file IO in the model package.
- Can you implement this with a design pattern we have covered? If so, try and use it.
- You need to make a decision on the parameter(s) and return value of
the buildPizzaConfig() method.
- Instructor or TA's will not help you with this decision. This decision will have consequences in the future modules.
Would you:
-
Pass in an instance of PizzaConfig?
public void buildPizzaConfig(String filename, PizzaConfig a1)
-
Return an instance of PizzaConfig?
public PizzaConfig buildPizzaConfig(String filename)
-
Do both?
public
PizzaConfig
buildPizzaConfig(String filename,
PizzaConfig
a1)
- Do something else? (Make sure to check this with your instructor or a TA.)
- You will then update the implementation for
configurePizzeria()
in ProxyPizzeria
so that given a text file name, this method now inserts an instance of your pizzeria
configuration into the list of pizzerias. Note that this method does not return the object.
- Add to your Exception package class(es) to handle and fix errors dealing with file I/O
errors. You should have at least 2 new ones.
Step 2: Support Multiple Users
We want to allow the system to be used by more than one person at a time and to do that
you will need to add support for concurrency (this is also called "thread-safe" code),
which can be done as follows:
- Design a class (or is is a hierarchy??) called
something like SimulatedUser that will edit data through your API using its own thread.
Put this class (or classes) in its own package called scaletests.
This class will be used to simulate multiple users (because we don't yet have a UI)
to test multi-threaded edits.
It is an area for class design and architecture, which you
should do on your own without detailed assistance from your TA.
Questions to consider:
- Where/how should the separate threads be created?
- Where should creating the threads NOT be done?
- How can it support different update options?
- How can it "break" without proper synchronization in the API?
Now code the new test driver class and test it as follows:
- Create a driver program for this unit that will:
- Instantiate a CreatePizzeria object.
- Create multiple SimulatedUser objects (reminder: each should be a separate thread)
that will modify the same list of pizzerias, using weak association (how should this be
coded?). These should perform the different operations available through your API.
- Test your implementation to verify that multiple threads updating the data at the same time fails by corrupting the data without synchronization in your API. As part of your testing documentation, turn in the results from this test run showing the failure.
- Add a test case that uses a threadpool to run two or more additional threads.
Update the your API to be thread-safe:
- After capturing the failure, add the synchronization to fix the problem to ensure that multiple threads altering same data at the same time now does
not cause data corruption. Test it with proper synchronization and turn in this version of the code (be sure to include these test results in your documentation).
- Be sure to use Version 1.1 (that has a static LHM object in your API) as
your starting point. Note: feel free to change your API to a a Singleton pattern if you want to try it.
- Consider synchronizing methods in classes that are used to setup the list of PizzaConfigs
(the LHM instance of PizzaConfig objects, which is a static object in the ProxyPizzerias class).
You will have to synchronize all methods used
for creating, reading, updating and deleting parts of PizzaConfig, OptionSet and
Option classes.
Step 3: Updates to your API
Note that to loop through elements in a LinkedHashMap you should use an Iterator.
- Add a method called printPizzerias() to your API that will print all the pizzerias.
- Update your API to allow deleting a Pizzeria with a method called deletePizzeria().
Where in your API does this go? Make sure to follow the existing design and code organization.
Important: How should you make these updates and follow our existing design for the API?
Design Considerations
Consideration 1
You have to synchronize methods in your Model package, and perhaps in others. Since the methods in OptionSet and
Option
class are protected you don't need to synchronize those methods. However some of the
methods in PizzaConfig will need to synchronized.
Do not just synchronize everything in PizzaCongig; make sure to think about what needs to be synchronized and at what level. Try both syntaxes in the appropriate places in the code.
Consideration 2
In this version I only expect you use the functionality provided through your ProxyPizzeria class.
Other than deleting a Pizzeria, do not overwhelm yourself with coding all possible cases since our
goal is to learn multi-threading.
Deliverables
- Updated code for each module:
- model
- wrapper
- exceptions
- io
- scaletests
- testcases (or what ever you called your test package)
- Test driver (your application).
- Updated UML class diagrams.
- Test data file(s).
- Test program showing the successful implementation of these classes.
- Test runs showing incorrect behavior without synchronization plus standard test output
showing correct behavior.
Put your test output in files that follow the naming convention of
PizzaTestAPI<desc>.txt, where <desc> is of your choosing.
Grading your submission
- Program Specification/Correctness
- Class diagram is provided.
- No errors, program always works correctly and meets the specification(s).
- The code could be reused as a whole or each routine could be reused.
- Multithreading is implemented in SimulatedUser class or other separate class.
- Synchronization is implemented in methods of PizzaConfig and/or other classes as appropriate.
- Demonstrates (code and test) necessary and proper synchronization usage. (In other words, removing synchronization
causes data corruption and you have test run output to demonstrate this).
- Test with both manually creating at least 2 threads and also using a thread pool.
- Readability
- No errors, code is clean, understandable, and well organized.
- Code has been packaged and authored based on Java coding standards.
- Documentation
- The documentation is well written and clearly explains what the code is
accomplishing and how.
- Design
- Code follows good design practices as discussed in class.
- Code demonstrates specific design patterns listed as requirements and/or as discussed in class.