I've created an Interface named "Writeable", and every class that implements it should override the method returnFilePath - which returns a String - a path to the correct file path related to that class.
for Example:
public class PatientRepository extends Observable implements Writeable,Readable{
private static final String filepath="src/files/patients.det";
...
#Override
public String returnFilePath() {
return filepath;
}
as you can see, this class also extends Observable. that class is observed by FileManager class:
public class FileManager implements Observer {
#Override
public void update(Observable o, Object arg) {
try (OutputStream fileOutputStream = new FileOutputStream(????);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)){
objectOutputStream.writeObject(arg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
what i want to do is that in the FileOutputStream constructor i'll pass the correct file path, according to the Object i'm currently working on, but I can't get access to the returnFilePath() method i wrote.
what should i do to fix that?
All i had to do is casting:
public class FileManager implements Observer {
#Override
public void update(Observable o, Object arg) {
try (OutputStream fileOutputStream = new FileOutputStream( ((Writeable)o).returnFilePath());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)){
objectOutputStream.writeObject(arg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Related
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:
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).
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;
Event has to be Serializable too.
Change
public abstract class Event {
to
public abstract class Event implements Serializable {
//Singleton
public class MainList implements Serializable {
private static MainList instance = new MainList();
private MainList() {}
public static MainList getInstance() {
return instance;
}
}
//Trying to deserialize..
public MainWindow() {
//Importing the latest version if it exists in path
MainList mainListObj = MainList.getInstance();
try {
FileInputStream fis = new FileInputStream(path);
ObjectInputStream oin = new ObjectInputStream(fis);
mainListObj = (MainList) oin.readObject(); //HERE//////
}
catch (Exception exc) {
return;
}
}
Singleton-Object of class MainList is serialized & can be found by path.
On the line //HERE/// object successfully deserializing to mainListObj,
BUT it's local..
How can I make it global? I think it could be solved by chanching getInstance method..somehow..
Your case is not singleton.
In your case, you can simply provide a setter.
static void setInstance(MainList newInstance) {
instance = newInstance;
}
MainList.setInstance(mainListObj);
i have this code:
File fileData = new File(this.getDataFolder(), "data.yml");
FileConfiguration data = YamlConfiguration.loadConfiguration(fileData);
data.options().copyDefaults(true);
try {
data.save(fileData);
} catch (IOException e) {
e.printStackTrace();
}
now my question is how i can do a instance of data in other class, i mean this is bukkit so i need that in another class i can execute the command data.save(fileData);
If you need that specific instance of FileConfiguration, the only way to have its reference in another class is using constructors, assuming the other class where you need this object is created in this class you could do something like so:
class YourClass{
File fileData;
FileConfiguration data;
#Override
void onEnable(){
fileData = new File(this.getDataFolder(), "data.yml");
data = YamlConfiguration.loadConfiguration(fileData);
data.options().copyDefaults(true);
try {
data.save(fileData);
} catch (IOException e) {
e.printStackTrace();
}
}
void theMethodWhereYouInstantiateTheOtherClass(){
OtherClass oc = new OtherClass();
}
}
And the the other class would look something like:
class OtherClass{
FileConfiguration data;
public OtherClass(FileConfiguration data){
this.data = data;
}
}
I declared an ISerializable interface in java.
I basically have 2 methods: serialize(), and deserialize(byte[] buffer).
public interface ISerializable{
byte[] serialize();
deserialize(byte[] buffer);
}
and here is an example of a class implementing this interface:
public class MySerializableClass implements ISerializable{
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
Ideally, I would like the call to deserailize to be implicit. i.e. when calling the constructor MySerializableClass(byte[] buffer), it would call the correct deserialize with the buffer passed. like that:
public abstract class AbstractSerializable {
public abstract byte[] serialize();
public abstract void deserialize(byte[] buffer);
public AbstractSerializable (){}
public AbstractSerializable (byte[] buffer){
deserialize();
}
}
public class MySerializableClass extends AbstractSerializable {
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
AFAIK it is problematic to call virtual methods within the constructor and this might end up with an undefined behavior.
so currently, I am doing the following:
MySerializableClass myClass = new MySerializableClass();
myClass.deserialize(buffer);
or by using a dedicated static method that is defined for each class that extends my interface (and basically just do the above 2 lines of code):
MySerializableClass myClass = MySerializableClass.CreateMySerializableClass(buffer);
My questions is: is there any elegant way to do that without the need to define a dedicated static method for each class implements ISerializable? Is there any design pattern that solves this issue?
Note: My serialization is unique so I need to write it on my own, and also for technical reasons I can only use very basic features of Java. ( no annotations,templaates metadata, etc.) so I need a very basic OOP solution.
I find your solution elegant enough, what you're doing is a Factory, which is an elegant way to solve your problem. You can keep your constructor private, and always retrieve the objects through the factories
public class MySerializableClass extends AbstractSerializable {
private MySerializableClass(){
}
public static MySerializableClass CreateMySerializableClass(final byte[] buffer){
MySerializableClass result = new MySerializableClass();
result.deserialize(buffer)
return result;
}
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
One another solution is you remove no-argument constructor so your concrete class must have to initialize with argument constructor .
I wouldn't pass byte arrays around when doing the serialization - instead I would use java.io.DataOutput and java.io.DataInput. You could then declare the interface ISerializable like e.g. so:
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
Then you could provide static utility methods, that are able to serialize and deserialize instances of ISerializable when provided with some DataOutput/DataInput. The static deserialize method could then also call a possible constructor, that accepts a DataInput as its only argument.
Here is a complete example code for this approach, that also includes a main method for testing the serialization:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SerializableTest {
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
/**
* Writes the given ISerializable to the given DataOutput.
*/
public static void writeSerializable(ISerializable s, DataOutput out) throws IOException{
writeClass(out, s.getClass());
s.serialize(out);
}
/**
* Reads an ISerializable from the given DataInput.
*/
public static ISerializable readSerializable(DataInput in, ClassLoader cl) throws IOException{
ISerializable element = null;
Class<?> c;
try {
c = readClass(in, cl);
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
try {
try {
// see if the class has a constructor that accepts a DataInput
Constructor<?> constructor= c.getDeclaredConstructor(DataInput.class);
constructor.setAccessible(true);
return (ISerializable)constructor.newInstance(in);
} catch (NoSuchMethodException e) {
//ignore
}
element = (ISerializable) newInstance(c);
element.deserialize(in);
} catch (Exception e) {
throw new IOException("Could not deserialize the class" + c.getName());
}
return element;
}
private static <T> T newInstance(Class<T> c) throws IOException {
T element = null;
Constructor<T> constructor;
try {
constructor = c.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
element = constructor.newInstance();
} catch (NoSuchMethodException | InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new IOException(e);
}
return element;
}
private static void writeClass(DataOutput out, Class<?> c) throws IOException {
out.writeUTF(c.getName());
}
private static Class<?> readClass(DataInput in, ClassLoader cl) throws IOException, ClassNotFoundException {
String name = in.readUTF();
return cl.loadClass(name);
}
// some test classes for testing serialization in the main method
public static class TestClass implements ISerializable{
private String data;
protected TestClass() {
// ISerializable no argument constructor
super();
}
public TestClass(String data) {
super();
this.data = data;
}
#Override
public void serialize(DataOutput out) throws IOException {
out.writeUTF(data);
}
#Override
public void deserialize(DataInput in) throws IOException {
this.data = in.readUTF();
}
}
public static class TestClass2 implements ISerializable{
private final String data;
protected TestClass2(DataInput in) throws IOException {
// ISerializable DataInput constructor
super();
this.data = in.readUTF();
}
public TestClass2(String data) {
super();
this.data = data;
}
#Override
public void serialize(DataOutput out) throws IOException {
out.writeUTF(data);
}
#Override
public void deserialize(DataInput in) throws IOException {
throw new UnsupportedOperationException();
}
}
// tests serialization and deserialization of two test classes
public static void main(String[] args) {
TestClass t1 = new TestClass("TestClass 1");
TestClass2 t2 = new TestClass2("TestClass 2");
File file = new File("testfile");
if (file.exists()) {
file.delete();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return;
}
DataOutputStream out = null;
try {
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
writeSerializable(t1, out);
writeSerializable(t2, out);
} catch (IOException e) {
e.printStackTrace();
return;
}finally{
if (out != null) {
try {
out.close();
} catch (IOException e) {}
}
}
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
ClassLoader cl = SerializableTest.class.getClassLoader();
TestClass loadedClass1 = (TestClass) readSerializable(in, cl);
TestClass2 loadedClass2 = (TestClass2) readSerializable(in, cl);
System.out.println("loadedClass1.data: " + loadedClass1.data);
System.out.println("loadedClass2.data: " + loadedClass2.data);
} catch (IOException e) {
e.printStackTrace();
return;
} finally{
if (in != null) {
try {
in.close();
} catch (IOException e) {}
}
}
}
}
Of course you will have the memory overhead of storing the class name together with the data, when using the static methods. But you can still call the serialize and deserialize methods manually, if this is a problem.
in my opinion you don't need to implement ISerializable interface with methods: serialize and deserialize. in any serializable class.
I think it would be better to have
interface ISerializer
{
byte[] serialize(ISerializable serializable);
ISerializable deserialize(byte[] buffer);
}
and have Serializer class which implements this ISerializer interface
ISerializable _ what to serialize or deserialize.
ISerializable will have methods, what serialization and deserialization need.
if serialization and deserialization don't need anything, it can be Object instead of ISerializable.
if you don't want casts after using deserialize method, this deserialize method can be generic:
T deserialize<T>( byte[] buffer );
I'm not sure if I'm asking this right, as I'm attempting to teach myself Java. I have a class which contains my main method, and within this class are several subclasses that need access to my user settings using java.util.Properties. I have to create the properties object in every subclass in order to make it work, and I can't reference the object using configFilePath, it must be null. I'm wondering if I can create this public object within the parent class, so I don't need to create it in all of its subclasses? Here is my code, I'm really not sure I'm doing this right although it works.
public class Frame1 extends JFrame {
Settings config = new Settings(); //this is the object I want to reference within subclasses
class Update extends SwingWorker<Integer, Void> { //first subclass
#Override
protected Integer doInBackground() throws Exception {
Settings config = new Settings(configFilePath); //yet I have to create the object within every subclass, this time an argument is required.
String templateDir = config.getProperty("templatedir");
String writePath = config.getProperty("outputdir");
//do some logic code, not required for my question
}
#Override
protected void done() {
Update2 update2 = new Update2();
update2.execute(); //start the next subclass which also needs access to Settings(configFilePath)
}
}
}
public class Settings extends JFrame {
String configFilePath = "C:/path/to/settings.properties";
Properties properties = new Properties();
public Settings(String configFilePath) throws IOException {
this.configFilePath = configFilePath;
FileInputStream fis = null;
try {
fis = new FileInputStream(configFilePath);
properties.load(fis);
} catch (FileNotFoundException e) {
setDefaults();
} finally {
if (fis != null) {
fis.close();
}
}
}
}
I'm not sure if I'm doing this right or not, it seems to work but seems to be rather redundant having to create the config object every time I need to access my user settings. I hope this hasn't been asked before, and if it has please link me, as I could not find it.
You can create the Setting class as a Singleton pattern, here is one example:
public class Settings extends JFrame{
String configFilePath = "C:/path/to/settings.properties";
Properties properties = new Properties();
private static Settings instance;
public static Settings getInstance(){
if(instance==null){
instance = new Setting();
}
return instance;
}
private Settings() throws IOException {
FileInputStream fis = null;
try {
fis = new FileInputStream(configFilePath);
properties.load(fis);
} catch (FileNotFoundException e) {
setDefaults();
} finally {
if (fis != null) {
fis.close();
}
}
}
}
Usage in any other class of your system:
Settings.getInstance().getProperty("...");
From Update you can use Frame1.this to access the this of Frame1 (because Update is an inner class of Frame1).
Then to access config you can use Frame1.this.config.
Here is a working example:
public class PrefixerFactory {
private String prefix; // Used by Prefixer
public PrefixerFactory(String prefix) {
this.prefix = prefix;
}
public Prefixer createPrefixer() {
return new Prefixer();
}
public class Prefixer { // Inner class
public String addPrefix(String value) {
// Using "prefix" from PrefixerFactory
return PrefixerFactory.this.prefix + value;
}
}
public static void main(String[] args) {
Prefixer helloPrefixer = new PrefixerFactory("Hello ").createPrefixer();
Prefixer goodbyePrefixer = new PrefixerFactory("Good bye ").createPrefixer();
System.out.println(helloPrefixer.addPrefix("world")); // Hello world
System.out.println(goodbyePrefixer.addPrefix("world")); // Good bye world
}
}