Part 7 Guidance

Problem

Your code currently writes the JSON or XML streams out to text files.  When the data is read back in it is very difficult to reconstruct java objects from those text streams.  In this next activity you are going to write and read the java objects directly to and from a file using java serialization.  You are going to update your code so that the java objects can be serialized to a file.

Once all your classes are Serializable, some thought needs to be given to how you implement the relationship between the objects, are they one way or two way, and what type of collections should be used.

Solution

Each file that needs to be Serialized must be marked as Serializable by implementing the Serializable interface, mark the Product class as Serializable

Product Class
public class Product    implements Serializable
{


Now implement the SerializableFileWriter

SerializableFileWriter
public class SerializableFileWriter   implements  OutputChannel
{
    private String  itsFileName;
    private ObjectOutputStream itsOutputStream;
    
    public  SerializableFileWriter()
    {
    }
    
    @Override
    public  void    openForWriting( String fname )
    {
        itsFileName = fname;
        
        try
        {
            itsOutputStream = new ObjectOutputStream(new FileOutputStream(fname));
        } catch (IOException ex)
        {
            Logger.getLogger(TextFileWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }    
    
    @Override
    public  void    write( Product theObject )
    {
        try
        {
            itsOutputStream.writeObject(theObject);
        } catch (IOException ex)
        {
            Logger.getLogger(SerializableFileWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }   
    
    
    @Override
    public  void    close()
    {
        try
        {
            itsOutputStream.close();
        } catch (IOException ex)
        {
            Logger.getLogger(SerializableFileWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

The write(...) method as we have declared it is restictive.  The API guide indicates that the signature for OutputStream.writeObject(Object stream) is an Object, so we can change the signature of the write(...) method to

SerializableFileWriter - updated
    @Override
    public  void    write( Object theObject )
    {
        try
        {
            itsOutputStream.writeObject(theObject);
        } catch (IOException ex)
        {
            Logger.getLogger(SerializableFileWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }   


Modify the Writer class so it has new factory method 

Writer.getSerializableFileWriter
public  static  SerializableFileWriter    getSerializableFileWriter( String fname )
{
    serializableWriter.openForWriting(fname);
        
    return serializableWriter;  
}


Implement a new class called SerializableFileReader with the following signatures.  Use this class to load the object from the filesystem into your code

SerializableFileReader
public class SerializableFileReader
{
    private String  itsFileName;
    private ObjectInputStream itsInputStream;
    
    public  SerializableFileReader()
    {
    }
    
    @Override
    public  void    openForReading( String fname )
    {
        itsFileName = fname;
        
        try
        {
            itsInputStream = new ObjectInputStream(new FileInputStream(fname));
        } catch (IOException ex)
        {
            Logger.getLogger(TextFileWriter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }    
    
    @Override
    public  Object    read()
    {
        Object result = null;
        try
        {
                result = itsInputStream.readObject();
        }catch (IOException |  ClassNotFoundException ex)
        {
            Logger.getLogger(TextFileReader.class.getName()).log(Level.SEVERE, null, ex);
        }
        finally
        {
            close();
        }
        return result;
    }   
    
    @Override
    public  void    close()
    {
        try
        {
            itsInputStream.close();
        } catch (IOException ex)
        {
            Logger.getLogger(TextFileReader.class.getName()).log(Level.SEVERE, null, ex);
        }
    }    
}


Refactoring

Updated version of InputChannel

InputChannel - updated
public interface InputChannel
{
    public  void    openForReading( String fname );
    public  Object  read();
    public  void    close();    
}


Modified version of SerializableFileReader

SerializableFileReader - updated
public class SerializableFileReader implements InputChannel
{


Implement the Reader class

Reader
public class Reader
{
    private static  final   TextFileReader  textFileReader = new TextFileReader();
    private static  final   SerializableFileReader  serializableReader = new SerializableFileReader();
    
    public  static  InputChannel    getTextFileReader( String fname )
    {
        textFileReader.openForReading(fname);
        
        return textFileReader;  
    }
    
    public  static  InputChannel    getSerializableFileReader( String fname )
    {
        serializableReader.openForReading(fname);
        
        return serializableReader;  
    }
}


In order to test our code, add the following lines of code the main() method

MainUnit.main()
    public static void main(String[] args)
    {
        MainUnit munit = new MainUnit();

        munit.bootstrap_products_items();
        munit.test_serialization_writer_all_products();
        munit.test_serialization_reader_all_products();

        munit.bootstrap_customers_accounts();
        munit.test_serialization_writer_all_customers();
        munit.test_serialization_reader_all_customers();

        System.out.println("=============================================================================");
        System.out.println("=============================================================================\n");
        
        munit.bootstrap_orders_accounts_items();
        munit.test_serialization_writer_all_objects();
        munit.test_serialization_reader_all_objects();
    }


Now implement each method

bootstrap_products_items
    public void bootstrap_products_items()
    {
        // This call will bootstrap the products
        Warehouse.getInstance();
    }


test_serialization_writer_all_products
    public void test_serialization_writer_all_products()
    {
        String fname = "c:\\tmp\\all-products.oos";

        // working with a Serializable File Writer
        SerializableFileWriter sfw = Writer.getSerializableFileWriter(fname);

        // Write the object out
        // HashMap are Serializable, so it can be written straight out
        // Because all objects in the HashMap are Serializable, they will all be
        // written out as well
        sfw.write( Warehouse.getInstance().getProducts());

        sfw.close();
    }


test_serialization_reader_all_products
    public void test_serialization_reader_all_products()
    {
        String fname = "c:\\tmp\\all-products.oos";
        // Read the object from the filesystem
        InputChannel sfr = Reader.getSerializableFileReader(fname);
        sfr.openForReading(fname);

        // Read the entire object network back in
        HashMap< String, Product> productsIn = (HashMap< String, Product>) sfr.read();

        // Only process the objects if the read was successful
        if (productsIn != null)
        {
            // Iterate over all the Products...
            for (Map.Entry<String, Product> entry : productsIn.entrySet())
            {
                Product pp = entry.getValue();
                System.out.printf("Product ==> %s, %f, %s, %d\n", pp.getDescription(), pp.getPrice(), pp.getProductCode(), pp.getQuantity());

                // Iterate over all the Items within each Product
                for (Map.Entry<Integer, Item> itemEntry : pp.getItems().entrySet())
                {
                    Item anItem = itemEntry.getValue();
                    System.out.printf("Item ==> %f, %d\n", anItem.getItemCost(), anItem.getUniqueId());
                }
            }
        }
    }


bootstrap_customers_accounts
    public void bootstrap_customers_accounts()
    {
        CustomerAccountMgr caMgr = CustomerAccountMgr.getInstance();
        // Create the accounts first and Customers
        Account acc = null;
        Customer cc = null;

        cc = new Customer("Sevlyn", "Wright", LocalDate.of(1965, 5, 8), "Somewhere in Birmingham");
        caMgr.addCustomer("C1", cc);
        acc = new Account(cc, 1);
        cc.setAccount(acc); // tie acc to the customer
        caMgr.addAccount(acc);

        cc = new Customer("Samuel", "Wright", LocalDate.of(1982, 4, 22), "Somewhere else in Birmingham");
        caMgr.addCustomer("C2", cc);
        acc = new Account(cc, 2);
        cc.setAccount(acc); // tie acc to the customer
        caMgr.addAccount(acc);

        cc = new Customer("Graham", "Meaden", LocalDate.of(1967, 4, 3), "Somewhere down South");
        caMgr.addCustomer("C3", cc);
        acc = new Account(cc, 3);
        cc.setAccount(acc); // tie acc to the customer
        caMgr.addAccount(acc);
    }


test_serialization_writer_all_customers
    public void test_serialization_writer_all_customers()
    {
        String fname = "c:\\tmp\\all-customers.oos";

        // working with a Serializable File Writer
        SerializableFileWriter sfw = Writer.getSerializableFileWriter(fname);

        // Write the object out
        // HashMap are Serializable, so it can be written straight out
        // Because all objects in the HashMap are Serializable, they will all be
        // written out as well
        sfw.write(CustomerAccountMgr.getInstance().getCustomers());

        // No need to write the accounts because they are linked to the customers
        sfw.close();
    }


test_serialization_reader_all_customers
    public void test_serialization_reader_all_customers()
    {
        String fname = "c:\\tmp\\all-customers.oos";
        // Read the object from the filesystem
        InputChannel sfr = Reader.getSerializableFileReader(fname);
        sfr.openForReading(fname);

        // Read the entire object network back in
        HashMap< String, Customer> customersIn = (HashMap< String, Customer>) sfr.read();

        // Only process the objects if the read was successful
        if (customersIn != null)
        {
            // Iterate over all the Customers...
            for (Map.Entry<String, Customer> entry : customersIn.entrySet())
            {
                Customer cc = entry.getValue();
                System.out.printf("Customer ==> %s, %s, %s, %s, %s\n",
                        entry.getKey(),
                        cc.getFname(),
                        cc.getLname(),
                        cc.getDob().toString(),
                        cc.getAddress());
                System.out.printf("\tAccount ==> %d, %s\n",
                        cc.getAccount().getAccountId(),
                        cc.getAccount().getDateCreated().toString());

            }
        }
    }


If the serialization is successful, you are now ready to implement bootstrap_orders_accounts_items(), this is our implementation

bootstrap_orders_accounts_items
    /*
     * This method will use an Order object to link the Item and Product objects 
     * to the Customer and Account objects
     */
    public void bootstrap_orders_accounts_items()
    {
        // Get an account and a product
        Account acc = CustomerAccountMgr.getInstance().getAccount(1);
        Product pp = Warehouse.getInstance().getProduct("JDF-001");

        // only proceed if the keys were valid
        if (acc != null && pp != null)
        {
            try
            {
                // create new order giving it the order number of 1
                Order oo = new Order(acc, 1, LocalDate.now());

                // add the order the account
                acc.addOrder(oo);

                // get an item from the product, remember Items represent real things
                // Products are descriptions of thise things
                Item itm = pp.getItem(1);

                // Set the quantity attribute, how many instances of the Item
                itm.setQuantity(3);

                // Only proceed if you used a valid item id
                if (itm != null)
                {
                    oo.addItem(itm);  // this call decrease the stock for this product
                }
            } catch (Exception ex)
            {
                Logger.getLogger(MainUnit.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }


test_serialization_writer_all_objects
    public void test_serialization_writer_all_objects()
    {
        String fname = "c:\\tmp\\all-objects.oos";

        // working with a Serializable File Writer
        SerializableFileWriter sfw = Writer.getSerializableFileWriter(fname);

        // Write the object out
        // HashMap are Serializable, so it can be written straight out
        // Because all objects in the HashMap are Serializable, they will all be
        // written out as well
        sfw.write(CustomerAccountMgr.getInstance().getCustomers());

        sfw.close();
    }


test_serialization_reader_all_objects
    public void test_serialization_reader_all_objects()
    {
        String fname = "c:\\tmp\\all-objects.oos";
        // Read the object from the filesystem
        InputChannel sfr = Reader.getSerializableFileReader(fname);
        sfr.openForReading(fname);

        // Read the entire object network back in
        HashMap< String, Customer> customersIn = (HashMap< String, Customer>) sfr.read();

        // Only process the objects if the read was successful
        if (customersIn != null)
        {
            // Iterate over all the Customers...
            for (Map.Entry<String, Customer> entry : customersIn.entrySet())
            {
                Customer cc = entry.getValue();
                System.out.printf("Customer ==> %s, %s, %s, %s, %s\n",
                        entry.getKey(),
                        cc.getFname(),
                        cc.getLname(),
                        cc.getDob().toString(),
                        cc.getAddress());
                System.out.printf("\tAccount ==> %d, %s\n",
                        cc.getAccount().getAccountId(),
                        cc.getAccount().getDateCreated().toString());

                displayAccountOrderDetais(cc.getAccount());
            }
        }
    }

    public void displayAccountOrderDetais(Account acc)
    {
        HashMap< Integer, Order> orders = acc.getOrders();
        for (Map.Entry<Integer, Order> entry : orders.entrySet())
        {
            Order ord = entry.getValue();
            System.out.printf("Order ==> %d, %f, %s\n",
                    ord.getOrderNo(),
                    ord.getTotalCost(),
                    ord.getDateCreated().toString());

            displayOrderDetails(ord);
        }
    }

    public void displayOrderDetails(Order ord)
    {
        for (Item itm : ord.getItems())
        {
            System.out.printf("Item ==> %d, %f, %d\n",
                    itm.getUniqueId(),
                    itm.getItemCost(),
                    itm.getQuantity());
            System.out.printf("Product ==> %s has %d in stock\n",
                    itm.getProduct().getDescription(),
                    itm.getProduct().getQuantity());
        }
    }
}