Suppose you have some AppendObjectOutputStream class (which is an ObjectOutputStream!) which overrides writeStreamHeader() like this:
#Override
public void writeStreamHeader() throws IOException
{
reset();
}
Now also, let's say you plan on saving multiple objects to a file; one object for each time your program runs. Would you, even on the first run, use AppendObjectOutputStream()?
You have to write the stream header first time with regular ObjectOutputStream otherwise you will get java.io.StreamCorruptedException on opening the file with ObjectInputStream.
public class Test1 implements Serializable {
public static void main(String[] args) throws Exception {
ObjectOutputStream os1 = new ObjectOutputStream(new FileOutputStream("test"));
os1.writeObject(new Test1());
os1.close();
ObjectOutputStream os2 = new ObjectOutputStream(new FileOutputStream("test", true)) {
protected void writeStreamHeader() throws IOException {
reset();
}
};
os2.writeObject(new Test1());
os2.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream("test"));
System.out.println(is.readObject());
System.out.println(is.readObject());
The above did not work for me, specifically the reset() did not work.
I found the following here:
https://coderanch.com/t/583191/java/ObjectOutputStream-appending-file-overiding-ObjectOutputStream
#Override
protected void writeStreamHeader() throws IOException {
// TODO Auto-generated method stub
System.out.println("I am called");
super.writeStreamHeader();
}
This worked for me. I know it seems counter-intuitive and at first blush it seems like calling the superclass method should not do anything, but it does. Read the original post and try it.
Related
I've a usecase where I'm creating an InputStream in one class & passing it to another. If I remove the finally block where I close the stream, it does not get detected in findbugs. Why is that?
Class A {
public static void methodA(InputStream is) {
// Do something.
// The stream is NOT closed.
}
}
Class B {
public void methodB(Sting filePath) {
FileInputStream fis = new FileInputStream(new File(filePath));
A.methodA(fis);
}
}
Ideally, findbugs should have detected that the stream is not closed in this use case. But, it doesn't & I'm curious to know why!
I need to append multiple objects in a single file in multiple sessions.
I searched for a fair amount of time and I got two solutions from here.
1) Using List to get already written objects from the file, add new objects to the List and rewriting the file.
2) Overriding writeStreamHeader()
I followed the second method i.e., overriding writeStreamHeader().
He stated that
A workaround is to subclass ObjectOutputStream and override
writeStreamHeader(). The overriding writeStreamHeader() should call
the super writeStreamHeader method if it is the first write to the
file and it should call ObjectOutputStream.reset() if it is appending
to a pre-existing ObjectOutputStream within the file.
So I tried this
class ObjectOutput extends ObjectOutputStream
{
protected ObjectOutput(OutputStream os) throws IOException, SecurityException {
super(os);
}
protected void writeStreamHeader() throws IOException
{
File file = new File("abc.txt");
if(file.exists())
{
reset();
}
else
{
file.createNewFile();
super.writeStreamHeader();
}
}
}
When I try to read the objects from the file there is an exception
Exception in thread "main" java.io.StreamCorruptedException: invalid stream header: 79737200
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
at Get.main(Get.java:11)
Then I tried this solution
Now, it worked perfectly!
So, what's wrong with the first code? I called the super.writeStreamHeader() when there is no file and the second method also calls the same method in the same scenario.
So,is there anything I am missing?
Thank you.
You can do this too and still it will work fine
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class MyObjectOutputStream extends ObjectOutputStream {
public MyObjectOutputStream() throws IOException{
super();
}
public MyObjectOutputStream(OutputStream outputStream) throws IOException{
super(outputStream);
}
public void writeStreamHeader(){}
}
The explanation for this is the same as the link to the solution you provided in the question itself.
I just stopped by a question about this subject.
Say when I do like this. (Note that I used "rw" not "rws" or "rwd").
try(RandomAccessFile raw = new RandomAccessFile(file, "rw")) {
// write some
}
Does RandomAccessFile#close() do as if I explicitly do getFD().sync()?
try(RandomAccessFile raw = new RandomAccessFile(file, "rw")) {
// write some
raw.getFD().sync();
}
I searched RandomAccessFile.java and the source just do
public void close() throws IOException {
// doin' here, things look like irrelevant
close0();
}
private native void close0() throws IOException;
Suppose you have some AppendObjectOutputStream class (which is an ObjectOutputStream!) which overrides writeStreamHeader() like this:
#Override
public void writeStreamHeader() throws IOException
{
reset();
}
Now also, let's say you plan on saving multiple objects to a file; one object for each time your program runs. Would you, even on the first run, use AppendObjectOutputStream()?
You have to write the stream header first time with regular ObjectOutputStream otherwise you will get java.io.StreamCorruptedException on opening the file with ObjectInputStream.
public class Test1 implements Serializable {
public static void main(String[] args) throws Exception {
ObjectOutputStream os1 = new ObjectOutputStream(new FileOutputStream("test"));
os1.writeObject(new Test1());
os1.close();
ObjectOutputStream os2 = new ObjectOutputStream(new FileOutputStream("test", true)) {
protected void writeStreamHeader() throws IOException {
reset();
}
};
os2.writeObject(new Test1());
os2.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream("test"));
System.out.println(is.readObject());
System.out.println(is.readObject());
The above did not work for me, specifically the reset() did not work.
I found the following here:
https://coderanch.com/t/583191/java/ObjectOutputStream-appending-file-overiding-ObjectOutputStream
#Override
protected void writeStreamHeader() throws IOException {
// TODO Auto-generated method stub
System.out.println("I am called");
super.writeStreamHeader();
}
This worked for me. I know it seems counter-intuitive and at first blush it seems like calling the superclass method should not do anything, but it does. Read the original post and try it.
I have the following class:
public class FileLoader {
private Map<Brand, String> termsOfUseText = new HashMap<Brand, String>();
public void load() {
for (Brand brand : Brand.values()) {
readAndStoreTermsOfUseForBrand(brand);
}
}
private void readAndStoreTermsOfUseForBrand(Brand brand) {
String resourceName = "termsOfUse/" + brand.name().toLowerCase() + ".txt";
InputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
try {
String content = IOUtils.toString(in);
termsOfUseText.put(brand, content);
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to find terms of use source file %s", resourceName),e);
}
}
public String getTextForBrand(Brand brand) {
return termsOfUseText.get(brand);
}
}
Brand is an enum, and I need all the valid .txt files to be on the classpath. How do I make the IOException occur, given that the Brand enum contains all the valid brands and therfore all the .txt files for them exist?
Suggestions around refactoring the current code are welcome if it makes it more testable!
Three options I see right off:
Use PowerMock to mock IOUtils.toString(). I consider PowerMock to be quite a last resort. I'd rather refactor the source to something a little more test-friendly.
Extract the IOUtils call to a protected method. Create a test-specific subclass of your class that overrides this method and throws the IOException.
Extract the InputStream creation to a protected method. Create a test-specific subclass to override the method and return a mock InputStream.
I would suggest a bit of refactoring. All your methods are void, this usually means they are not functional.
For example, you can extract this functionality:
private String readTermsOfUseForBrand(InputStream termsOfUserIs) {
try {
String content = IOUtils.toString(in);
return content;
} catch (IOException e) {
throw new IllegalStateException(String.format("Failed to find terms of use source file %s", resourceName), e);
}
return null;
}
So that we can assert on the String result in our tests.
Of course this is not functional code, as it reads from an Input Stream. And it does so with IOUtils.toString() method that cannot be mocked easily (well, there's PowerMock but as Ryan Stewart said it's the last resort).
To test IO exceptions you can create a failing input stream (tested with JDK7):
public class FailingInputStream extends InputStream {
#Override
public int read() throws IOException {
throw new IOException("Test generated exception");
}
}
And test like that:
#Test
public void testReadTermsOfUseForBrand() {
FileLoader instance = new FileLoader();
String result = instance.readTermsOfUseForBrand(new FailingInputStream());
assertNull(result);
}
Missing file will cause NullPointerException because getResourceAsStream will return null and you will have in==null. IOException in this case may actually be pretty rare. If it's critical for you to see it, I can only think of instrumenting this code to throw it if code is executed in test scope. But is it really that important?
I would use a mock to accomplish this.
Example (untested, just to give you some thought):
#Test(expected=IllegalStateException.class)
public void testThrowIOException() {
PowerMockito.mockStatic(IOUtils.class);
PowerMockito.when(IOUtils.toString()).thenThrow(
new IOException("fake IOException"));
FileLoader fileLoader = new FileLoader();
Whitebox.invokeMethod(fileLoader,
"readAndStoreTermsOfUseForBrand", new Brand(...));
// If IllegalStateException is not thrown then this test case fails (see "expected" above)
}
Code below is completely untested
To cause the IOException use:
FileInputStream in = this.getClass().getClassLoader().getResourceAsStream(resourceName);
in.mark(0);
//read some data
in.reset(); //IOException
To test the IOException case use:
void test
{
boolean success = false;
try
{
//code to force ioException
}
catch(IOException ioex)
{
success = true;
}
assertTrue(success);
}
In JUnit4
#Test(expected=IOException.class)
void test
{
//code to force ioException
}
Other JUnit
void test
{
try
{
//code to force IOException
fail("If this gets hit IO did not occur, fail test");
}
catch(IOException ioex)
{
//success!
}
}