Add txt files to a runnable JAR file - java

I'm trying to make a runnable jar file and I'm having problems with my .txt files.
My program also have images, but fortunately I've figured out how to manage them. I'm using something like this with them and it works just fine both Eclipse and the jar:
logoLabel.setIcon(new ImageIcon(getClass().getResource("/logo.png")));
My problem is when I've something like this in one of my classes:
try {
employeeList = (TreeSet<Employee>) ListManager.readFile("list/employeeList.txt");
} catch (ClassNotFoundException i) {
i.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
}
And this in the class ListManager that I use to read my lists serialized in the .txt files:
public static Object readFile(String file) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
Object o = is.readObject();
is.close();
return o;
}
I also have a similar method to write in the files.
I've tried several combinations that I've found here:
How to include text files with Executable Jar
Creating Runnable Jar with external files included
Including a text file inside a jar file and reading it
I've also tried with slash, without slash, using openStream, not using openStream... But or I get a NullPointerException or it doesn't compile at all...
Maybe is something silly or maybe is a concept error that I've of how URL class works, I'm new to programming...
Thank you very much in advance for your advice!
EDIT:
It's me again... The answer Raniz gave was just what I needed and it worked perfect, but now my problem is with the method that I use to write in the files...
public static void writeFile(Object o, String file) throws IOException {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file));
os.writeObject(o);
os.close();
}
try {
ListManager.writeFile(employeeList.getEmployeeList(), "lists/employeeList.txt");
} catch (IOException i) {
i.printStackTrace();
}
Could you help me please? I don't know what I should use to replace FileOutputStream, because I think there is the problem again, am I right?
Thank you very much!

The problem is that you're trying to access a file inside of a JAR archive as a file in the file system (because that's what FileInputStream is for) and that won't work.
You can convert readFile to use an URL instead and let URL handle opening the stream for you:
public static Object readFile(URL url) throws IOException, ClassNotFoundException {
ObjectInputStream is = new ObjectInputStream(url.openStream());
Object o = is.readObject();
is.close();
return o;
}
You should also put your code in a try-statement since it currently doesn't close the streams if an IOException occurs:
public static Object readFile(URL url) throws IOException, ClassNotFoundException {
try(ObjectInputStream is = new ObjectInputStream(url.openStream())) {
Object o = is.readObject();
return o;
}
}
try {
employeeList = (TreeSet<Employee>) ListManager.readFile(getClass().getResource("/list/employeeList.txt"));
} catch (ClassNotFoundException i) {
i.printStackTrace();
} catch (IOException i) {
i.printStackTrace();
}
I also have a similar method to write in the files.
That won't work if the files are inside the JAR so you should probably consider having your files outside your JAR.

Yes, if you want to read resources from inside a jar file, you shouldn't use FileInputStream. Perhaps you should add a readResource method:
public static Object readResource(Class clazz, String resource)
throws IOException, ClassNotFoundException {
try (ObjectInputStream is =
new ObjectInputStream(clazz.getResourceAsStream(resource))) {
return is.readObject();
}
}
(I'd also suggest updating your readFile method to use a try-with-resources block - currently if there's an exception you won't close the stream...)
Note that when you say "I also have a similar method to write in the files" - you won't be able to easily write to a resource in the jar file.

Related

Serialization/deserialization java adding objects to existing array list in saved file [duplicate]

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.

How to output a temporary file in API service in Java, and delete it in the end?

I am writing an API service that fetches data from a stream, and outputs it in a file. I can't output it as a stream because I use Swagger (now OpenAPI) 2.0, which doesn't support output streams (Swagger 3.0 does, but i can't use it).
What would be the cleanest way to make a file, output it via the service, and then make sure it gets deleted?
I initially thought I might use a temp file and delete in finally clause. However, there is no guarantee that the file finished downloading on the client side before that clause is reached and file is deleted.
Am I right? Wrong? Is there a better way to do this?
I was talking about using a closeable in the comments. This is it.
Usage:
try (TempFile file = new TempFile("tempfile", ".txt")) {
// do stuff with file
} catch (IOException e) {
// error handling.
// file should be automatically deleted.
}
TempFile:
public class TempFile implements AutoCloseable {
private final File file;
public TempFile(String prefix, String suffix) {
this.file = File.createTempFile(prefix, suffix);
}
public File getFile() {
return this.file;
}
#Override
public void close() throws IOException {
this.file.delete();
}
}

How to watch file for new content and retrieve that content

I have a file with name foo.txt. This file contains some text. I want to achieve following functionality:
I launch program
write something to the file (for example add one row: new string in foo.txt)
I want to get ONLY NEW content of this file.
Can you clarify the best solution of this problem? Also I want resolve related issues: in case if I modify foo.txt I want to see diff.
The closest tool which I found in Java is WatchService but if I understood right this tool can only detect type of event happened on filesystem (create file or delete or modify).
Java Diff Utils is designed for that purpose.
final List<String> originalFileContents = new ArrayList<String>();
final String filePath = "C:/Users/BackSlash/Desktop/asd.txt";
FileListener fileListener = new FileListener() {
#Override
public void fileDeleted(FileChangeEvent paramFileChangeEvent)
throws Exception {
// use this to handle file deletion event
}
#Override
public void fileCreated(FileChangeEvent paramFileChangeEvent)
throws Exception {
// use this to handle file creation event
}
#Override
public void fileChanged(FileChangeEvent paramFileChangeEvent)
throws Exception {
System.out.println("File Changed");
//get new contents
List<String> newFileContents = new ArrayList<String> ();
getFileContents(filePath, newFileContents);
//get the diff between the two files
Patch patch = DiffUtils.diff(originalFileContents, newFileContents);
//get single changes in a list
List<Delta> deltas = patch.getDeltas();
//print the changes
for (Delta delta : deltas) {
System.out.println(delta);
}
}
};
DefaultFileMonitor monitor = new DefaultFileMonitor(fileListener);
try {
FileObject fileObject = VFS.getManager().resolveFile(filePath);
getFileContents(filePath, originalFileContents);
monitor.addFile(fileObject);
monitor.start();
} catch (InterruptedException ex) {
ex.printStackTrace();
} catch (FileNotFoundException e) {
//handle
e.printStackTrace();
} catch (IOException e) {
//handle
e.printStackTrace();
}
Where getFileContents is :
void getFileContents(String path, List<String> contents) throws FileNotFoundException, IOException {
contents.clear();
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"));
String line = null;
while ((line = reader.readLine()) != null) {
contents.add(line);
}
}
What I did:
I loaded the original file contents in a List<String>.
I used Apache Commons VFS to listen for file changes, using FileMonitor. You may ask, why? Because WatchService is only available starting from Java 7, while FileMonitor works with at least Java 5 (personal preference, if you prefer WatchService you can use it). note: Apache Commons VFS depends on Apache Commons Logging, you'll have to add both to your build path in order to make it work.
I created a FileListener, then I implemented the fileChanged method.
That method load new contents form the file, and uses Patch.diff to retrieve all differences, then prints them
I created a DefaultFileMonitor, which basically listens for changes to a file, and I added my file to it.
I started the monitor.
After the monitor is started, it will begin listening for file changes.

Getting StreamCorruptedException after closing OutputStream [duplicate]

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.

How do I load resources when using a classloader?

I'm using JarFile and JarURLConnection to load files out of a jar file. I'm then taking the classes, and loading them via BCEL (ByteCode Engineering Library, apache library). I cant just directly use a class loader because im modifying some classes slightly with the BCEL. I need to load the classes by their bytes into my bcel loader. However, one of the classes I'm loading references a resource. This resource is inside of the jar, so I can get the file (When iterating over the entries in the JarFile, I ignore the regular files, and take the class files for loading later). But just having the file won't do me any good, as the class loads it as a resource. Is there any way I can take that resource from the jar (well I can take it and load it into a byte[], the next part is the issue) and dynamically add it as a resource for my program, so that the classes that I load wont be missing their resources?
Got a lot of stuff here, if anythings confusing, ask in comments, I might've said something wrong, or missed something altogether :) Thanks
I'll show a little of my class loader here (extends ClassLoader):
#Override
public URL getResource(String name) {
System.out.println("LOADING RESOURCE: " + name);
try {
return new URL(null, name, new Handler(files));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Now, it is printing out "LOADING RESOURCE: filename", but its then giving me a MalformedURLException (I have no protocol atm, just a file path, that's not a true valid path, but it's just an attempt to give it to my Handler class below).
class Handler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
#Override
public void connect() throws IOException {
}
#Override
public InputStream getInputStream() throws IOException {
System.out.println("IS: " + url);
return /*method to get input steam*/;
}
};
}
}
The /*method to get input steam*/ is set in my real code, but that's not relevant here. So any further ideas with this?

Categories

Resources