DPSD - Project 1.2

Making your Pizzeria Configuration Scale with File I/O and Multithreading

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:

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.

  1. 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.
  2. 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.

  3. 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().
  4. 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".

  5. When creating the class/method(s) for reading data from a text file you should follow these guidelines:
  6. 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.

  7. 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:

  1. 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:

    1. Where/how should the separate threads be created?
    2. Where should creating the threads NOT be done?
    3. How can it support different update options?
    4. How can it "break" without proper synchronization in the API?

  2. Now code the new test driver class and test it as follows:
    1. Create a driver program for this unit that will:
      1. Instantiate a CreatePizzeria object.
      2. 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.
    2. 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.
    3. Add a test case that uses a threadpool to run two or more additional threads.

  3. Update the your API to be thread-safe:
    1. 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).
    2. 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.
    3. 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.

  1. Add a method called printPizzerias() to your API that will print all the pizzerias.
  2. 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

  1. Updated code for each module:
    • model
    • wrapper
    • exceptions
    • io
    • scaletests
    • testcases (or what ever you called your test package)
  2. Test driver (your application).
  3. Updated UML class diagrams.
  4. Test data file(s).
  5. Test program showing the successful implementation of these classes.
  6. 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

  1. Program Specification/Correctness
    1. Class diagram is provided.
    2. No errors, program always works correctly and meets the specification(s).
    3. The code could be reused as a whole or each routine could be reused.
    4. Multithreading is implemented in SimulatedUser class or other separate class.
    5. Synchronization is implemented in methods of PizzaConfig and/or other classes as appropriate.
    6. 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).
    7. Test with both manually creating at least 2 threads and also using a thread pool.
  2. Readability
    1. No errors, code is clean, understandable, and well organized.
    2. Code has been packaged and authored based on Java coding standards.
  3. Documentation
    1. The documentation is well written and clearly explains what the code is accomplishing and how.
  4. Design
    1. Code follows good design practices as discussed in class.
    2. Code demonstrates specific design patterns listed as requirements and/or as discussed in class.