I have been trying to create a class that can be set as Standard Out and that writes to a File. So far, no problem. However, when I try to write some Japanese into the output it won't show up in the files. All I get are lines of '?' characters.
This is how my OutputStream class looks like (I create an instance of it and hands that over to "System.setOut(OutputStream)" in my main method).
public class MyStdOutStream extends OutputStream {
private OutputStreamWriter out;
public MyStdOutStream(File file) throws IOException {
out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
}
#Override
public void close() throws IOException {
out.flush();
out.close();
super.close(); //No idea if this line does anything...
}
#Override
public void write(int nr) throws IOException {
out.write(nr);
}
}
I then accept input through a JTextField (while testing, it will be a different source later), and print that both with "System.out.println(String)" and also appending it to a JTextArea. It shows up as proper Japanese in the JTextArea, but not in the File.
Does anyone know what I am doing wrong? I have tested with all available Charsets in the StandardCharsets import without success.
EDIT: It is an assignment, and I am not allowed to use anything other than the Standard Java Library (so, no imported JARs)
Related
Is it not possible to append to an ObjectOutputStream?
I am trying to append to a list of objects. Following snippet is a function that is called whenever a job is finished.
FileOutputStream fos = new FileOutputStream
(preferences.getAppDataLocation() + "history" , true);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject( new Stuff(stuff) );
out.close();
But when I try to read it I only get the first in the file.
Then I get java.io.StreamCorruptedException.
To read I am using
FileInputStream fis = new FileInputStream
( preferences.getAppDataLocation() + "history");
ObjectInputStream in = new ObjectInputStream(fis);
try{
while(true)
history.add((Stuff) in.readObject());
}catch( Exception e ) {
System.out.println( e.toString() );
}
I do not know how many objects will be present so I am reading while there are no exceptions. From what Google says this is not possible. I was wondering if anyone knows a way?
Here's the trick: subclass ObjectOutputStream and override the writeStreamHeader method:
public class AppendingObjectOutputStream extends ObjectOutputStream {
public AppendingObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
#Override
protected void writeStreamHeader() throws IOException {
// do not write a header, but reset:
// this line added after another question
// showed a problem with the original
reset();
}
}
To use it, just check whether the history file exists or not and instantiate either this appendable stream (in case the file exists = we append = we don't want a header) or the original stream (in case the file does not exist = we need a header).
Edit
I wasn't happy with the first naming of the class. This one's better: it describes the 'what it's for' rather then the 'how it's done'
Edit
Changed the name once more, to clarify, that this stream is only for appending to an existing file. It can't be used to create a new file with object data.
Edit
Added a call to reset() after this question showed that the original version that just overrode writeStreamHeader to be a no-op could under some conditions create a stream that couldn't be read.
As the API says, the ObjectOutputStream constructor writes the serialization stream header to the underlying stream. And this header is expected to be only once, in the beginning of the file. So calling
new ObjectOutputStream(fos);
multiple times on the FileOutputStream that refers to the same file will write the header multiple times and corrupt the file.
Because of the precise format of the serialized file, appending will indeed corrupt it. You have to write all objects to the file as part of the same stream, or else it will crash when it reads the stream metadata when it's expecting an object.
You could read the Serialization Specification for more details, or (easier) read this thread where Roedy Green says basically what I just said.
The easiest way to avoid this problem is to keep the OutputStream open when you write the data, instead of closing it after each object. Calling reset() might be advisable to avoid a memory leak.
The alternative would be to read the file as a series of consecutive ObjectInputStreams as well. But this requires you to keep count how many bytes you read (this can be implementd with a FilterInputStream), then close the InputStream, open it again, skip that many bytes and only then wrap it in an ObjectInputStream().
I have extended the accepted solution to create a class that can be used for both appending and creating new file.
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class AppendableObjectOutputStream extends ObjectOutputStream {
private boolean append;
private boolean initialized;
private DataOutputStream dout;
protected AppendableObjectOutputStream(boolean append) throws IOException, SecurityException {
super();
this.append = append;
this.initialized = true;
}
public AppendableObjectOutputStream(OutputStream out, boolean append) throws IOException {
super(out);
this.append = append;
this.initialized = true;
this.dout = new DataOutputStream(out);
this.writeStreamHeader();
}
#Override
protected void writeStreamHeader() throws IOException {
if (!this.initialized || this.append) return;
if (dout != null) {
dout.writeShort(STREAM_MAGIC);
dout.writeShort(STREAM_VERSION);
}
}
}
This class can be used as a direct extended replacement for ObjectOutputStream.
We can use the class as follows:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ObjectWriter {
public static void main(String[] args) {
File file = new File("file.dat");
boolean append = file.exists(); // if file exists then append, otherwise create new
try (
FileOutputStream fout = new FileOutputStream(file, append);
AppendableObjectOutputStream oout = new AppendableObjectOutputStream(fout, append);
) {
oout.writeObject(...); // replace "..." with serializable object to be written
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
How about before each time you append an object, read and copying all the current data in the file and then overwrite all together to file.
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 have a prolog file (Expert System) that I consult from Java using Jpl libraries (org.jpl7.*) and I have an UI where I want to show the output of prolog's queries.
This is my Custom Output Stream that should redirect every console content into my interface (jTextAreaOUTPUT is the place where i redirect the content)
public class CustomOutputStream extends OutputStream {
private JTextArea jTextAreaOUTPUT;
public CustomOutputStream(JTextArea textArea) {
jTextAreaOUTPUT = textArea;
}
#Override
public void write(int b) throws IOException {
// redirects data to the text area
jTextAreaOUTPUT.append(String.valueOf((char)b));
// scrolls the text area to the end of data
jTextAreaOUTPUT.setCaretPosition(jTextAreaOUTPUT.getDocument().getLength());
}
}
This are some lines I have in my Interface Class: this calls the Custom Output Stream methond:
PrintStream printStream = new PrintStream(new CustomOutputStream(jTextAreaOUTPUT), true, "UTF-8");
// keeps reference of standard output stream
PrintStream standardOut = System.out;
System.setOut(printStream);
System.setErr(printStream);
For some strange reasons it doesn't work whith this prolog file (I tried with other and It works): UI freezes and content keeps showing in java console (eclipse).
The Expert System file works with write instruction in Prolog (e.g. write('Lorem Ipsum') )
Why standardOut in never used ? Is it ok declared this way?
Is there a way to force redirect for all the text that should be written in eclipse console?
I also tried to use " write Stream " method in prolog, but (only for this prolog file, maybe due to recursion) UI freezes even though outpus is written on a txt file.
You might need to override the other write functions in outputstream write(byte[] b), write(byte[] b, int off, int len) if the writer doesn't write one character at a time
To override the other write functions of OutputStream just provide similar code to the single character function you already wrote:
public class CustomOutputStream extends OutputStream {
private JTextArea jTextAreaOUTPUT;
public CustomOutputStream(JTextArea textArea) {
jTextAreaOUTPUT = textArea;
}
#Override
public void write(int b) throws IOException {
// redirects data to the text area
jTextAreaOUTPUT.append(String.valueOf((char) b));
// scrolls the text area to the end of data
jTextAreaOUTPUT.setCaretPosition(jTextAreaOUTPUT.getDocument().getLength());
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
// redirects data to the text area
jTextAreaOUTPUT.append(new String(b, off, len));
// scrolls the text area to the end of data
jTextAreaOUTPUT.setCaretPosition(jTextAreaOUTPUT.getDocument().getLength());
}
#Override
public void write(byte[] b) throws IOException {
write(b,0,b.length);
}
}
Is it not possible to append to an ObjectOutputStream?
I am trying to append to a list of objects. Following snippet is a function that is called whenever a job is finished.
FileOutputStream fos = new FileOutputStream
(preferences.getAppDataLocation() + "history" , true);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject( new Stuff(stuff) );
out.close();
But when I try to read it I only get the first in the file.
Then I get java.io.StreamCorruptedException.
To read I am using
FileInputStream fis = new FileInputStream
( preferences.getAppDataLocation() + "history");
ObjectInputStream in = new ObjectInputStream(fis);
try{
while(true)
history.add((Stuff) in.readObject());
}catch( Exception e ) {
System.out.println( e.toString() );
}
I do not know how many objects will be present so I am reading while there are no exceptions. From what Google says this is not possible. I was wondering if anyone knows a way?
Here's the trick: subclass ObjectOutputStream and override the writeStreamHeader method:
public class AppendingObjectOutputStream extends ObjectOutputStream {
public AppendingObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
#Override
protected void writeStreamHeader() throws IOException {
// do not write a header, but reset:
// this line added after another question
// showed a problem with the original
reset();
}
}
To use it, just check whether the history file exists or not and instantiate either this appendable stream (in case the file exists = we append = we don't want a header) or the original stream (in case the file does not exist = we need a header).
Edit
I wasn't happy with the first naming of the class. This one's better: it describes the 'what it's for' rather then the 'how it's done'
Edit
Changed the name once more, to clarify, that this stream is only for appending to an existing file. It can't be used to create a new file with object data.
Edit
Added a call to reset() after this question showed that the original version that just overrode writeStreamHeader to be a no-op could under some conditions create a stream that couldn't be read.
As the API says, the ObjectOutputStream constructor writes the serialization stream header to the underlying stream. And this header is expected to be only once, in the beginning of the file. So calling
new ObjectOutputStream(fos);
multiple times on the FileOutputStream that refers to the same file will write the header multiple times and corrupt the file.
Because of the precise format of the serialized file, appending will indeed corrupt it. You have to write all objects to the file as part of the same stream, or else it will crash when it reads the stream metadata when it's expecting an object.
You could read the Serialization Specification for more details, or (easier) read this thread where Roedy Green says basically what I just said.
The easiest way to avoid this problem is to keep the OutputStream open when you write the data, instead of closing it after each object. Calling reset() might be advisable to avoid a memory leak.
The alternative would be to read the file as a series of consecutive ObjectInputStreams as well. But this requires you to keep count how many bytes you read (this can be implementd with a FilterInputStream), then close the InputStream, open it again, skip that many bytes and only then wrap it in an ObjectInputStream().
I have extended the accepted solution to create a class that can be used for both appending and creating new file.
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class AppendableObjectOutputStream extends ObjectOutputStream {
private boolean append;
private boolean initialized;
private DataOutputStream dout;
protected AppendableObjectOutputStream(boolean append) throws IOException, SecurityException {
super();
this.append = append;
this.initialized = true;
}
public AppendableObjectOutputStream(OutputStream out, boolean append) throws IOException {
super(out);
this.append = append;
this.initialized = true;
this.dout = new DataOutputStream(out);
this.writeStreamHeader();
}
#Override
protected void writeStreamHeader() throws IOException {
if (!this.initialized || this.append) return;
if (dout != null) {
dout.writeShort(STREAM_MAGIC);
dout.writeShort(STREAM_VERSION);
}
}
}
This class can be used as a direct extended replacement for ObjectOutputStream.
We can use the class as follows:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ObjectWriter {
public static void main(String[] args) {
File file = new File("file.dat");
boolean append = file.exists(); // if file exists then append, otherwise create new
try (
FileOutputStream fout = new FileOutputStream(file, append);
AppendableObjectOutputStream oout = new AppendableObjectOutputStream(fout, append);
) {
oout.writeObject(...); // replace "..." with serializable object to be written
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
How about before each time you append an object, read and copying all the current data in the file and then overwrite all together to file.
In my application I download several critical files from a server, and I want to write some code that handles the case where the a file download didn't complete for a reason or other ,to retry downloading it at next startup. The function that downloads a file at a time however throws only MalformedURLException and IOException , but if these exceptions are thrown that means that the download didn't even begin. How should I arrange things so I can treat the case where a download failed , even if it began ?
void download(String file) throws MalformedURLException ,IOException
{
BufferedInputStream getit = new BufferedInputStream(new URL(file).openStream());
FileOutputStream saveit = new FileOutputStream(DOWNLOAD_PATH+fileName+"."+ZIP_EXTENSION);
BufferedOutputStream bout = new BufferedOutputStream(saveit,1024);
byte data[] = new byte[1024];
int readed = getit.read(data,0,1024);
while(readed != -1)
{
bout.write(data,0,readed);
readed = getit.read(data,0,1024);
}
bout.close();
getit.close();
saveit.close();
}
You might be better using the Jakarta Commons HttpClient API.
However, for your custom function take a look at InterruptedIOException and bytesTransferred on https://docs.oracle.com/javase/1.5.0/docs/api/java/io/InterruptedIOException.html
public class InterruptedIOException
extends IOException
Signals that an I/O operation has been interrupted.
The field bytesTransferred indicates how many bytes were successfully transferred before the interruption occurred.
If your download is synchronous, you should be able to add an appropriate exception (or return an appropriate value) to indicate failure.
If your download is asynchronous, consider using the observer pattern. You can pass in an observer implementation as an extra parameter to your download method.
The observer in your case (for example) might look something like:
public interface FileDownloadObserver
{
public void downloadFailed(String file, Object error);
public void downloadSucceeded(String file);
}
Then the download method would look like:
void download(String file, FileDownloadObserver observer)
throws MalformedURLException, IOException
This is all assuming you can actually detect that the download failed. If not, you might have to provide some more information about how you're doing the download.