This class is a iterator like container for ObjectOutputStream, that also delegates to factories if there are no object in the stream, and has a static method to write object to a file. There are two kind of factories allowed, callable and class (uses no arg constructors).
What is the utility?
The read() methods are generic method with inference, so you can do this. It’s not actually type safe, however serialization can never be.
ObjectsReader it = new ObjectsReader(programStateLocation);
Car c;
Integer i;
try {
//if you need to use another constructor than the no-args, or don't want to use reflection.
i = it.readOrReturn(new Integer(3));
app = it.readOrLazyCreate(Car.class);
} finally {
close(it);
}
//to write back.
ObjectsReader.writeObjects(programStateLocation, i, c);
package util.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.channels.FileChannel;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class reads objects from a file.
* It also needs to be close()-ed.
* It has a static method providing the inverse operation,
* writing a set of objects to a file.
*/
public final class ObjectsReader implements Closeable {
private final File objectLocation;
private ObjectInputStream stream;
private FileChannel innerChannel;
private final long fileSize;
/**
* @param file contains the objects or doesn't exist.
* @throws NullPointerException if the file is null.
*/
public ObjectsReader(File file) {
super();
this.objectLocation = file;
this.fileSize = file.length();
try {
FileInputStream innerStream = new FileInputStream(objectLocation);
innerChannel = innerStream.getChannel();
stream = new ObjectInputStream(new BufferedInputStream(innerStream));
} catch (IOException ex) {
}
}
public boolean hasNext() {
try {
return innerChannel != null && stream != null && innerChannel.position() < fileSize;
} catch (IOException ex) {
return false;
}
}
/**
* Use this method if you want to use
* the a constructor other than the no-args
* one or don't want to use reflection.
* Can use laziness or not according to the
* ClassCallable implementation.
* @param <T> return type
* @param instance the factory of the return lazy or not,
* in case of read error.
* @throws NullPointerException if the factory given is null.
* @throws IllegalArgumentException
* if the expected type
* is not on the stream and the factory throws
* a exception.
* @return a object of the return type
*/
public <T> T readOrLazyCreate(ClassCallable<T> factory) {
Class<T> expectedClass = factory.getReturnClass();
Object raw = readObjectQuietly();
if (raw != null && expectedClass.isInstance(raw)) {
return (T) raw;
}
try {
return factory.call();
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
/**
* Use this method if you want to use
* the no arg constructor and don't mind
* using reflection.
* @param <T> return type
* @param instance the class of the lazy return
* in case of read error.
* @throws NullPointerException if the class given is null.
* @throws AssertionError
* if the expected type is not on the stream
* and there is no public no-args constructor.
* @return a object of the return type
*/
public <T> T readOrLazyCreate(Class<T> factory) {
Object raw = readObjectQuietly();
if (raw != null && factory.isInstance(raw)) {
return (T) raw;
}
//try with the class no args constructor instead.
try {
return (T) factory.newInstance();
} catch (Exception ex) {
Error e = new AssertionError("The type " + factory + " couldn't be instantiated");
e.initCause(ex);
throw e;
}
}
/**
* Use this method if you don't want lazyness
* for the return value in case of read error.
* @param <T> return type
* @param instance the value of the return in
* case of read error.
* @throws NullPointerException if instance given is null.
* @return a object of the return type
*/
public <T> T readOrReturn(T instance) {
Object raw = readObjectQuietly();
if (raw != null && instance.getClass().isInstance(raw)) {
return (T) raw;
}
return instance;
}
private Object readObjectQuietly() {
try {
return (stream == null)? null : stream.readObject();
} catch (IOException ex) {
Logger.getLogger(ObjectsReader.class.getName()).log(Level.SEVERE, ex.getClass()+" "+ex.getMessage());
} catch (ClassNotFoundException ex) {
Logger.getLogger(ObjectsReader.class.getName()).log(Level.SEVERE, ex.getClass()+" "+ex.getMessage());
}
return null;
}
@Override
public void close() throws IOException {
if (stream != null) {
stream.close();
}
}
/**
* If possible writes objects. Allows null objects
*/
public static void writeObjects(File objectLocation, Serializable... obj) {
ObjectOutputStream s = null;
try {
s = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(objectLocation)));
for (Serializable a : obj) {
s.writeObject(a);
}
s.flush();
} catch (Exception ex) {
Logger.getLogger(IoUtils.class.getName()).log(Level.SEVERE, "Couldn't write object", ex);
} finally {
if (s != null) {
try {
s.close();
} catch (IOException ex) {
Logger.getLogger(ObjectsReader.class.getName()).log(Level.SEVERE, "Couldn't close stream", ex);
}
}
}
}
public static abstract class ClassCallable<T> implements Callable<T> {
Class c;
public ClassCallable(Class<T> given){
if(given == null)
throw new IllegalArgumentException("given class cannot be null");
c = given;
}
public Class<T> getReturnClass(){
return c;
}
}
}