0

New to this topic and right now I'm stuck at a brick wall. I have 2 classes, parent class: Controller.java and subclass: GreenhouseControls.java. I need to serialize a GreenhouseControls object but also an instance variable (eventList) from its superclass Controller.java.

My serialization happens when an inner class of GreenhouseControls.java throws a custom ControllerException, which is caught in the main method. Before terminating the program, the GreenhouseControls object should be saved (including the field from its superclass).

Why is a NotSerializableException thrown by the inner class WindowMalfunction of GreenhouseControls? Anyone have any ideas, as I am seriously stuck?

What I tried is the following:

  1. Implement serializable on Controller.java. This is because if the superclass is serializable, then subclass is automatically serializable, however this throws java.io.NotSerializableException: GreenhouseControls$WindowMalfunction, (WindowMalfunction is the inner class that throws the initial exception to begin the serialization processs).
  2. Implement serializable on GreenhouseControls.java and implement custom serialization by overriding writeObject() and readObject() to save the field from the superclass. This approach yet again throws the same exception as the approach 1.
private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(super.eventList);
}

private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        in.defaultReadObject();
        Object obj = in.readObject();
        List<Event> x = cast(obj);
        super.eventList = x;
}

Controller.java

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

public class Controller {
    // THIS IS THE VARIABLE I NEED TO SAVE
    protected List<Event> eventList = new ArrayList<Event>();

    public void addEvent(Event c) {
        eventList.add(c);
    }

    public void run() throws ControllerException {
        while (eventList.size() > 0)
            // Make a copy so you're not modifying the list
            // while you're selecting the elements in it:
            for (Event e : new ArrayList<Event>(eventList))
                if (e.ready()) {
                    System.out.println(e);
                    e.action();
                    eventList.remove(e);
                }
    }

    public static void shutDown() { }

}

GreenhouseControls.java class (note I have removed the inner classes and other code from it and only left related info)

public class GreenhouseControls extends Controller implements Serializable {

    private int errorcode = 0;

    public class WindowMalfunction extends Event {

        public WindowMalfunction(long delayTime) {
            super(delayTime);
        }

        public void action() throws ControllerException {
            windowok = false;
            throw new ControllerException("Window malfunction");
        }

        public String toString() {
            return "Window malfunction";
        }
    }

    public class PowerOut extends Event {

        public PowerOut(long delayTime) {
            super(delayTime);
        }

        public void action() throws ControllerException {
            poweron = false;
            throw new ControllerException("Power out");
        }

        public String toString() {
            return "Power out";
        }
    }

    // Various other inner classes that extend event exist

    public static void serializeObject(GreenhouseControls gc) {
        FileOutputStream fileOut;
        ObjectOutputStream out;
        try {
            fileOut = new FileOutputStream("dump.out");
            out = new ObjectOutputStream(fileOut);
            out.writeObject(gc);
            System.out.println("WERRROR code: " + gc.getError());
            out.close();
            fileOut.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(super.eventList);
    }

    private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        in.defaultReadObject();
        Object obj = in.readObject();
        List<Event> x = cast(obj);
        super.eventList = x;
    }

    @SuppressWarnings("unchecked")
    public static <T extends List<?>> T cast(Object obj) {
        return (T) obj;
    }

    public int getError() {
        return errorcode;
    }

    public Fixable getFixable(int errorcode) {
        switch (errorcode) {
            case 1:
                return new FixWindow();
            case 2:
                return new PowerOn();
            default:
                return null;
        }
    }

    public static void main(String[] args) {
        GreenhouseControls gc = null;

        try {
            String option = args[0];
            String filename = args[1];

            if (!(option.equals("-f")) && !(option.equals("-d"))) {
                System.out.println("Invalid option");
                printUsage();
            }

            // gc = new GreenhouseControls();

            if (option.equals("-f")) {
                gc = new GreenhouseControls();
                gc.addEvent(gc.new Restart(0, filename));
            }

            gc.run();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Invalid number of parameters");
            printUsage();
        } catch (ControllerException e) {
            String errormsg;
            if (e.getMessage().equals("Window malfunction")) {
                gc.errorcode = 1;
                errormsg = "Window malfunction event occurred Error code: " + gc.errorcode;
            } else {
                gc.errorcode = 2;
                errormsg = "Power out event occurred Error code: " + gc.errorcode;
            }

            logError(errormsg);
            serializeObject(gc);
            gc.displayEventList();
            shutDown();
        }
    }

}

Event.java

public abstract class Event {
    private long eventTime;
    protected final long delayTime;

    public Event(long delayTime) {
        this.delayTime = delayTime;
        start();
    }

    public void start() { // Allows restarting
        eventTime = System.currentTimeMillis() + delayTime;
    }

    public boolean ready() {
        return System.currentTimeMillis() >= eventTime;
    }

    public abstract void action() throws ControllerException;
ires
  • 17
  • 4

1 Answers1

2

Event has to be Serializable too.

Change

public abstract class Event {

to

public abstract class Event implements Serializable {
Bill Mair
  • 1,073
  • 6
  • 15
  • oh wow that was it, could you please explain why Event should be as well? – ires Feb 14 '23 at 18:52
  • https://stackoverflow.com/questions/21476401/what-happens-if-your-serializable-class-contains-a-member-which-is-not-serializa#:~:text=It'll%20throw%20a%20NotSerializableException,that%20field%20a%20transient%20field.&text=Save%20this%20answer.,-Show%20activity%20on – paulk23 Feb 14 '23 at 18:56
  • 1
    During serialization all the object classes are given an ID (`static final long serialVersionUID`). If this isn't there the ObjectStream doesn't know how label it when writing or identify it while reading. So the fields `eventTime` and `delayTime` can't be saved either, their owner "Event" doesn't have an id with being tagged Serializable and `serialVersionUID` is automatically generated if not defined by you. – Bill Mair Feb 14 '23 at 18:58