Redirecting FileOutputStream to console - Java - java

I'd like to rewrite and simply my code to cut down on the number of methods in a class that do exactly the same thing but either write to a file, or to a console so I can do things like:
PrintFlightSchedule(String aFileName); // prints to a file
PrintFlightSchedule(); // writes to console.
I've tried creating the following test method just to demonstrate what I'my trying to achieve, by defining an abstract OutputStream, then instantiating it as either a PrintStream, or console (via System.out):
public static void testOutputStream(String fileNm, String msg) {
OutputStream os;
if (fileNm.equals("") ) { // No file name provided, write to console
os = System.out;
}
// File name provided, write to this file name
else {
try {
os = new FileOutputStream(fileNm);
}
catch (FileNotFoundException fe) {
System.out.println("File not found " + fe.toString());
}
}
// Use the output stream here - ideally println method?
// os.println or write(6);
}
This is admittedly half-assed, but it gives you an idea what I'd like to achieve.
Is there a way in Java to define the output method (file or console) at run-time, so I can use the same methods to do either, at runtime? I guess a simple way would be to redirect the FileOutputStream to the console - is that possible?

Basically, you need to create a method that simply takes a OutputStream and writes all the details to it...
Then you create some helper methods that simply call it with the appropriate stream...
public void printFlightSchedule(OutputStream os) throws IOException {
// Write...
}
public void printFlightSchedule(File file) throws IOException {
FileOutputStream fis = null;
try {
fis = new FileOutputStream(file);
printFlightSchedule(fis);
} finally {
try {
} catch (Exception e) {
}
}
}
public void printFlightSchedule() throws IOException {
printFlightSchedule(System.out);
}
You may also want to take look at the Code Conventions for the Java Language...It will make it easier for people to read and understand your code ;)

java.io.OutputStream is already an abstraction of 'something you can write bytes to'. If your class interacts with an OutputStream and the clients of your class can choose what that OutputStream actually is (a file, the console, a null device, ...) then your class won't need to care about the type of OutpuStream is actually needed for a given context.
So instead of your class trying to do what it needs to do and create OutputStreams for its clients, let it just focus on its true responsibility and let clients provide the OutputStream they desire.
So keep only one constructor :
/**
* Constructs a new instance that will print to the given OutputStream
*/
PrintFlightSchedule(OutputStream stream);

Don't provide a filename String as a parameter, but a Writer.
Your method's signature becomes
void PrintFlightSchedule(Writer writer);
The code you show would be the bit that creates the Writer on startup depending on runtime parameters:
public static Writer createOutputWriter(String fileNm) {
OutputStream os;
if (fileNm.equals("") ) { // No file name provided, write to console
os = System.out;
}
// File name provided, write to this file name
else {
try {
os = new FileOutputStream(fileNm);
}
catch (FileNotFoundException fe) {
System.out.println("File not found " + fe.toString());
}
}
return new BufferedWriter(new OutputStreamWriter(os));
}
Don't forget to flush the writer after output.
How to write to Standard Output using BufferedWriter

You can create a FileOutputStream with a FileDescriptor instead of a string.
public FileOutputStream(FileDescriptor fdObj)
Creates a file output stream to write to the specified file descriptor, which represents an existing connection to an actual file in the file system.
First, if there is a security manager, its checkWrite method is called with the file descriptor fdObj argument as its argument.
If fdObj is null then a NullPointerException is thrown.
This constructor does not throw an exception if fdObj is invalid. However, if the methods are invoked on the resulting stream to attempt I/O on the stream, an IOException is thrown.
And default FileDescriptors are:
static FileDescriptor err
A handle to the standard error stream.
static FileDescriptor in
A handle to the standard input stream.
static FileDescriptor out
A handle to the standard output stream.
So the equivalent should be:
public static void testOutputStream(String fileNm, String msg) {
FileOutputStream os;
if (fileNm.equals("") ) { // No file name provided, write to console
os = new FileOutputStream(FileDescriptor.out);
}
// File name provided, write to this file name
else {
try {
os = new FileOutputStream(fileNm);
}
catch (FileNotFoundException fe) {
System.out.println("File not found " + fe.toString());
}
}
// Use the output stream here - ideally println method?
// os.println or write(6);
}

Related

Java how to test file utility that are void methods?

How exactly would I test classes or methods that involve writing out files, or moving files around from directory to directory? Let's say I have this helper method as one of Spring MVC's Service layer methods:
private void writeFileOut(String fileContents, String fileName) throws IOException {
File fullFilePath;
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
try {
fullFilePath = new File("/temp/directory/" + fileName);
fileWriter = new FileWriter(fullFilePath);
bufferedWriter = new BufferedWriter(fileWriter);
if (bufferedWriter != null) {
bufferedWriter.append(fileContents);
}
} catch (IOException e) {
LOGGER.info("Error writing file out: " + e.getMessage());
throw e;
} finally {
try {
bufferedWriter.close();
} catch (IOException e) {
throw e;
}
}
}
How exactly is this method testable? It isn't producing anything and I can't think of a way to test this is working exactly.
First things first: why reinvent the wheel instead of using Files.writeString?
Assuming you are looking for a more general solution to test code that touches the file system: I'd try to keep any external resource (network, database, other processes, file system) out of my unit tests. You'll end up with brittle tests that depend on e.g., file system details (latency, cleanup between tests, tests running in parallel may step on each others toes).
Then: please use try with resources:
private void writeFileOut(String fileContents, String fileName) throws IOException {
File fullFilePath = new File("/temp/directory/" + fileName);
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(fullFilePath))) {
bufferedWriter.append(fileContents);
}
catch (IOException e) {
LOGGER.info("Error writing file out: " + e.getMessage());
throw e;
}
}
Now your code does 3 things:
create the path for the file
write the given data to the file
log if an error occurs
It's often easiest to move code you want to test into new methods that you can call separately, instead of having to test through all the other code. So isolate pieces of code, especially the code that tries to use the file system.
private void writeFileOut(String fileContents, String fileName) throws IOException {
File fullFilePath = new File(makeTempPath(fileName));
try (Writer bufferedWriter = makeWriter(fullFilePath)) {
bufferedWriter.append(fileContents);
}
catch (IOException e) {
LOGGER.info("Error writing file out: " + e.getMessage());
throw e;
}
}
String makeTempPath(String fileName) {
return "/temp/directory/" + fileName;
}
Writer makeWriter(String fullFilePath) {
return new BufferedWriter(new FileWriter(fullFilePath));
}
Now you can test makeTempPath separately.
You can mock makeWriter when you test writeFileOut. You can check that it receives what it was supposed to receive. You can have it throw to trigger the error handling.
When you mock, you can use a framework like Mockito or you create a derived class for the methods you want to mock and override them. Note that makeWriter returns a Writer. In real life this is the BufferedWriter that writes to a file. In testing you can return a StringWriter to capture what gets written.
Either way, be careful not to mock too much, or you may end up just testing your mocks, not the production code.
to test writing out files: Better to use file Comparison i.e have reference file(expected file) and compare it with the generated one.
for your reference: Comparing text files with Junit
moving files around from directory to directory: after moving file u can check for existance of file eg. file.exists() in the destination directory and also of course u can verify the contents also.
Eg: File file = new File("file Path in destination folder");
assertTrue("File doesnot exit",file.exists());

How can I open a file in java without its contents been removed?

I want my program to create a file for the user (just for the first time) and write some information to it (it's not just a line and also can be adjusted anytime later). So I did this:
public void write() {
try {
file = new File("c:\\Users\\Me\\Desktop\\text.txt");
if(!file.exists()) // I found this somewhere on the internet for File class
file.createNewFile(); // not to remove contents. I have no idea if it works
writer = new Formatter(file);
} catch(Exception e) {
e.printStackTrace();
}
writer.format("%s %s ", nameInput.getText(),lastNameInput.getText());
writer.close();
}
It works but there some problems:
When the file gets opened later, as default, the File class removes its contents.
When the information is written to the file and Formatter got closed, next time somewhere else in the program when I use it again to write to the file, the information gets updated and not added to the previous ones. And if I don't close it, it won't write.
first af all, this code here:
if(!file.exists())
file.createNewFile();
it only creates a new file in case it doesn't exists in your path.
To write on your file without overwriting it I recommend you to do this:
FileWriter fileWriter;
public void write() {
try {
file = new File("c:\\Users\\Me\\Desktop\\text.txt");
if(!file.exists())
file.createNewFile();
// use a FileWriter to take the file to write on
fileWriter = new FileWriter(file, true); // true means that you do not overwrite the file
writer = new Formatter(fileWriter); // than you put your FileWriter in the Formatter
} catch(Exception e) {
e.printStackTrace();
}
writer.format("%s %s ", nameInput.getText(),lastNameInput.getText());
writer.close();
}
Hope this was helpfull! :)
As people mentioned above, I had to pass the file through the constructor of FileWriter class. this way my first problem got solved (I mentioned them in the question) and for the second one, I had to reopen the Formatter whenever I wanted to add more.
public void write() {
try {
writer = new Formatter(new FileWriter(file,true);
} catch(Exception e) {
e.printStackTrace();
}
writer.format("%s %s ", nameInput.getText(),lastNameInput.getText());
writer.close(); }
creation and initialization of file should be done once and outside the method.

Unexpected amount of lines when writing to a csv file

A part of my application writes data to a .csv file in the following way:
public class ExampleWriter {
public static final int COUNT = 10_000;
public static final String FILE = "test.csv";
public static void main(String[] args) throws Exception {
try (OutputStream os = new FileOutputStream(FILE)){
os.write(239);
os.write(187);
os.write(191);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
for (int i = 0; i < COUNT; i++) {
writer.write(Integer.toString(i));
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(checkLineCount(COUNT, new File(FILE)));
}
public static String checkLineCount(int expectedLineCount, File file) throws Exception {
BufferedReader expectedReader = new BufferedReader(new FileReader(file));
try {
int lineCount = 0;
while (expectedReader.readLine() != null) {
lineCount++;
}
if (expectedLineCount == lineCount) {
return "correct";
} else {
return "incorrect";
}
}
finally {
expectedReader.close();
}
}
}
The file will be opened in excel and all kind of languages are present in the data. The os.write parts are for prefixing the file with a byte order mark as to enable all kinds of characters.
Somehow the amount of lines in the file do not match the count in the loop and I can not figure out how. Any help on what I am doing wrong here would be greatly appreciated.
You simply need to flush and close your output stream (forcing fsync) before opening the file for input and counting. Try adding:
writer.flush();
writer.close();
inside your try-block. after the for-loop in the main method.
(As a side note).
Note that using a BOM is optional, and (in many cases) reduces the portability of your files (because not all consuming app's are able to handle it well). It does not guarantee that the file has the advertised character encoding. So i would recommend to remove the BOM. When using Excel, just select the file and and choose UTF-8 as encoding.
You are not flushing the stream,Refer oracle docs for more info
which says that
Flushes this output stream and forces any buffered output bytes to be
written out. The general contract of flush is that calling it is an
indication that, if any bytes previously written have been buffered by
the implementation of the output stream, such bytes should immediately
be written to their intended destination. If the intended destination
of this stream is an abstraction provided by the underlying operating
system, for example a file, then flushing the stream guarantees only
that bytes previously written to the stream are passed to the
operating system for writing; it does not guarantee that they are
actually written to a physical device such as a disk drive.
The flush method of OutputStream does nothing.
You need to flush as well as close the stream. There are 2 ways
manually call close() and flush().
use try with resource
As I can see from your code that you have already implemented try with resource and also BufferedReader class also implements Closeable, Flushable so use code as per below
public static void main(String[] args) throws Exception {
try (OutputStream os = new FileOutputStream(FILE); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))){
os.write(239);
os.write(187);
os.write(191);
for (int i = 0; i < COUNT; i++) {
writer.write(Integer.toString(i));
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(checkLineCount(COUNT, new File(FILE)));
}
When COUNT is 1, the code in main() will write a file with two lines, a line with data plus an empty line afterwards. Then you call checkLineCount(COUNT, file) expecting that it will return 1 but it returns 2 because the file has actually two lines.
Therefore if you want the counter to match you must not write a new line after the last line.
(As another side note).
Notice that writing CSV-files the way you are doing is really bad practice. CSV is not so easy as it may look at first sight! So, unless you really know what you are doing (so being aware of all CSV quirks), use a library!

How to write multiple times in external console application using java?

I need to dialogue with an external c++ console program (read output and write input). I read from the application with a Thread (and it works), but when it needs input, it works only the first time, then the stream probably remains empty, and it doesn't receive the second input (and external program closes).
The application i'm using is a simple .exe wrote in c++ that:
print "Insert first input"
scan input1
print input1
print "Insert second input"
scan input2
print input2
Main class:
import java.io.*;
import java.util.Scanner;
public class ExampleCom {
public static Communication com = new Communication();
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
String s;
com.read();
while(true)
{
s = in.nextLine();
com.write(s);
}
}
Communication class:
public class Communication
{
Process p;
OutputStream writer;
public InputStream reader = null;
Read r; //Class that with a loop read all exe input
Communication()
{
try{
p = Runtime.getRuntime ().exec ("C:\\esempio.exe");
writer = p.getOutputStream();
reader = p.getInputStream();
}catch(Exception e){}
}
public void read()
{
r = new Read();
Thread threadRead = new Thread(r);
threadRead.start();
}
public void write(String s)
{
try{
writer.write(s.getBytes());
writer.flush();
writer.close();
}catch(Exception e){}
}
}
How can I send my string (like "writer.write('hello')") when the external application needs it?
The problem is that in your write() method, you have the line
writer.close();
which means that after calling it the first time, you are closing the input stream to your C++. As far as it is concerned, it sees the "end of file" marker after your first input.
What you should do is put the close() in a separate method, and call that method only when you are done working with that process.
Now, as your target program expects text input and will only interpret the input if it gets an end-of-line (as per your answer to the question in my comment), you should supply that end-of-line to it.
Instead of doing raw byte-writes, I think a better approach would be to use a PrintWriter for that output stream, and use as naturally as you use System.out.println(). It can also save you on the flush() part.
You are interpreting it incorrectly when you see that your program is not reading the input until you close(). It's not waiting - it sends it as soon as you call flush(). But the C++ waits for either an end-of-file or an end-of-line, and since you are not giving it an end-of-line, then only close(), that sends it end-of-file, causes it to accept the input. But then you can no longer send any further data.
So the solution is, first, to define your writer as a PrintWriter. Instead of
OutputStream writer;
Use
PrintWriter writer;
And instead of
writer = p.getOutputStream();
Use
writer = new PrintWriter(p.getOutputStream(), true);
The true there will give you auto-flush whenever you use the println() command.
Now, your write method should be:
public void write(String s)
{
writer.println(s);
}
Note that a PrintWriter doesn't produce exceptions, so if you care about errors, you have to check for them using checkError().
And of course, have the close() in a separate method, as I mentioned before.
Because the write() method might throw an IOException, it is advisable to call the close() method inside a finally block.Place the writer.close() method outside the try clause:
finally {
if(writer != null) {
writer.close();
}

file.delete() returns false even though file.exists(), file.canRead(), file.canWrite(), file.canExecute() all return true

I'm trying to delete a file, after writing something in it, with FileOutputStream. This is the code I use for writing:
private void writeContent(File file, String fileContent) {
FileOutputStream to;
try {
to = new FileOutputStream(file);
to.write(fileContent.getBytes());
to.flush();
to.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
As it is seen, I flush and close the stream, but when I try to delete, file.delete() returns false.
I checked before deletion to see if the file exists, and: file.exists(), file.canRead(), file.canWrite(), file.canExecute() all return true. Just after calling these methods I try file.delete() and returns false.
Is there anything I've done wrong?
Another bug in Java. I seldom find them, only my second in my 10 year career. This is my solution, as others have mentioned. I have nether used System.gc(). But here, in my case, it is absolutely crucial. Weird? YES!
finally
{
try
{
in.close();
in = null;
out.flush();
out.close();
out = null;
System.gc();
}
catch (IOException e)
{
logger.error(e.getMessage());
e.printStackTrace();
}
}
It was pretty odd the trick that worked. The thing is when I have previously read the content of the file, I used BufferedReader. After reading, I closed the buffer.
Meanwhile I switched and now I'm reading the content using FileInputStream. Also after finishing reading I close the stream. And now it's working.
The problem is I don't have the explanation for this.
I don't know BufferedReader and FileOutputStream to be incompatible.
I tried this simple thing and it seems to be working.
file.setWritable(true);
file.delete();
It works for me.
If this does not work try to run your Java application with sudo if on linux and as administrator when on windows. Just to make sure Java has rights to change the file properties.
Before trying to delete/rename any file, you must ensure that all the readers or writers (for ex: BufferedReader/InputStreamReader/BufferedWriter) are properly closed.
When you try to read/write your data from/to a file, the file is held by the process and not released until the program execution completes. If you want to perform the delete/rename operations before the program ends, then you must use the close() method that comes with the java.io.* classes.
As Jon Skeet commented, you should close your file in the finally {...} block, to ensure that it's always closed. And, instead of swallowing the exceptions with the e.printStackTrace, simply don't catch and add the exception to the method signature. If you can't for any reason, at least do this:
catch(IOException ex) {
throw new RuntimeException("Error processing file XYZ", ex);
}
Now, question number #2:
What if you do this:
...
to.close();
System.out.println("Please delete the file and press <enter> afterwards!");
System.in.read();
...
Would you be able to delete the file?
Also, files are flushed when they're closed. I use IOUtils.closeQuietly(...), so I use the flush method to ensure that the contents of the file are there before I try to close it (IOUtils.closeQuietly doesn't throw exceptions). Something like this:
...
try {
...
to.flush();
} catch(IOException ex) {
throw new CannotProcessFileException("whatever", ex);
} finally {
IOUtils.closeQuietly(to);
}
So I know that the contents of the file are in there. As it usually matters to me that the contents of the file are written and not if the file could be closed or not, it really doesn't matter if the file was closed or not. In your case, as it matters, I would recommend closing the file yourself and treating any exceptions according.
There is no reason you should not be able to delete this file. I would look to see who has a hold on this file. In unix/linux, you can use the lsof utility to check which process has a lock on the file. In windows, you can use process explorer.
for lsof, it's as simple as saying:
lsof /path/and/name/of/the/file
for process explorer you can use the find menu and enter the file name to show you the handle which will point you to the process locking the file.
here is some code that does what I think you need to do:
FileOutputStream to;
try {
String file = "/tmp/will_delete.txt";
to = new FileOutputStream(file );
to.write(new String("blah blah").getBytes());
to.flush();
to.close();
File f = new File(file);
System.out.print(f.delete());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
It works fine on OS X. I haven't tested it on windows but I suspect it should work on Windows too. I will also admit seeing some unexpected behavior on Windows w.r.t. file handling.
If you are working in Eclipse IDE, that could mean that you haven't close the file in the previous launch of the application. When I had the same error message at trying to delete a file, that was the reason. It seems, Eclipse IDE doesn't close all files after termination of an application.
Hopefully this will help. I came across similar problem where i couldn't delete my file after my java code made a copy of the content to the other folder. After extensive googling, i explicitly declared every single file operation related variables and called the close() method of each file operation object, and set them to NULL. Then, there is a function called System.gc(), which will clear up the file i/o mapping (i'm not sure, i just tell what is given on the web sites).
Here is my example code:
public void start() {
File f = new File(this.archivePath + "\\" + this.currentFile.getName());
this.Copy(this.currentFile, f);
if(!this.currentFile.canWrite()){
System.out.println("Write protected file " +
this.currentFile.getAbsolutePath());
return;
}
boolean ok = this.currentFile.delete();
if(ok == false){
System.out.println("Failed to remove " + this.currentFile.getAbsolutePath());
return;
}
}
private void Copy(File source, File dest) throws IOException {
FileInputStream fin;
FileOutputStream fout;
FileChannel cin = null, cout = null;
try {
fin = new FileInputStream(source);
cin = fin.getChannel();
fout = new FileOutputStream(dest);
cout = fout.getChannel();
long size = cin.size();
MappedByteBuffer buf = cin.map(FileChannel.MapMode.READ_ONLY, 0, size);
cout.write(buf);
buf.clear();
buf = null;
cin.close();
cin = null;
fin.close();
fin = null;
cout.close();
cout = null;
fout.close();
fout = null;
System.gc();
} catch (Exception e){
this.message = e.getMessage();
e.printStackTrace();
}
}
the answer is when you load the file, you need apply the "close" method, in any line of code, works to me
There was a problem once in ruby where files in windows needed an "fsync" to actually be able to turn around and re-read the file after writing it and closing it. Maybe this is a similar manifestation (and if so, I think a windows bug, really).
None of the solutions listed here worked in my situation. My solution was to use a while loop, attempting to delete the file, with a 5 second (configurable) limit for safety.
File f = new File("/path/to/file");
int limit = 20; //Only try for 5 seconds, for safety
while(!f.delete() && limit > 0){
synchronized(this){
try {
this.wait(250); //Wait for 250 milliseconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
limit--;
}
Using the above loop worked without having to do any manual garbage collecting or setting the stream to null, etc.
The problem could be that the file is still seen as opened and locked by a program; or maybe it is a component from your program that it had been opened in, so you have to ensure you use the dispose() method to solve that problem.
i.e. JFrame frame;
....
frame.dispose();
You have to close all of the streams or use try-with-resource block
static public String head(File file) throws FileNotFoundException, UnsupportedEncodingException, IOException
{
final String readLine;
try (FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
LineNumberReader lnr = new LineNumberReader(isr))
{
readLine = lnr.readLine();
}
return readLine;
}
if file.delete() is sending false then in most of the cases your Bufferedreader handle will not be closed. Just close and it seems to work for me normally.
I had the same problem on Windows. I used to read the file in scala line by line with
Source.fromFile(path).getLines()
Now I read it as a whole with
import org.apache.commons.io.FileUtils._
// encoding is null for platform default
val content=readFileToString(new File(path),null.asInstanceOf[String])
which closes the file properly after reading and now
new File(path).delete
works.
FOR Eclipse/NetBeans
Restart your IDE and run your code again this is only trick work for me after one hour long struggle.
Here is my code:
File file = new File("file-path");
if(file.exists()){
if(file.delete()){
System.out.println("Delete");
}
else{
System.out.println("not delete");
}
}
Output:
Delete
Another corner case that this could happen: if you read/write a JAR file through a URL and later try to delete the same file within the same JVM session.
File f = new File("/tmp/foo.jar");
URL j = f.toURI().toURL();
URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();
// open a Jar entry in auto-closing manner
try (InputStream i = c.getInputStream()) {
// just read some stuff; for demonstration purposes only
byte[] first16 = new byte[16];
i.read(first16);
System.out.println(new String(first16));
}
// ...
// i is now closed, so we should be good to delete the jar; but...
System.out.println(f.delete()); // says false!
Reason is that the internal JAR file handling logic of Java, tends to cache JarFile entries:
// inner class of `JarURLConnection` that wraps the actual stream returned by `getInputStream()`
class JarURLInputStream extends FilterInputStream {
JarURLInputStream(InputStream var2) {
super(var2);
}
public void close() throws IOException {
try {
super.close();
} finally {
// if `getUseCaches()` is set, `jarFile` won't get closed!
if (!JarURLConnection.this.getUseCaches()) {
JarURLConnection.this.jarFile.close();
}
}
}
}
And each JarFile (rather, the underlying ZipFile structure) would hold a handle to the file, right from the time of construction up until close() is invoked:
public ZipFile(File file, int mode, Charset charset) throws IOException {
// ...
jzfile = open(name, mode, file.lastModified(), usemmap);
// ...
}
// ...
private static native long open(String name, int mode, long lastModified,
boolean usemmap) throws IOException;
There's a good explanation on this NetBeans issue.
Apparently there are two ways to "fix" this:
You can disable the JAR file caching - for the current URLConnection, or for all future URLConnections (globally) in the current JVM session:
URL u = new URL("jar:" + j + "!/META-INF/MANIFEST.MF");
URLConnection c = u.openConnection();
// for only c
c.setUseCaches(false);
// globally; for some reason this method is not static,
// so we still need to access it through a URLConnection instance :(
c.setDefaultUseCaches(false);
[HACK WARNING!] You can manually purge the JarFile from the cache when you are done with it. The cache manager sun.net.www.protocol.jar.JarFileFactory is package-private, but some reflection magic can get the job done for you:
class JarBridge {
static void closeJar(URL url) throws Exception {
// JarFileFactory jarFactory = JarFileFactory.getInstance();
Class<?> jarFactoryClazz = Class.forName("sun.net.www.protocol.jar.JarFileFactory");
Method getInstance = jarFactoryClazz.getMethod("getInstance");
getInstance.setAccessible(true);
Object jarFactory = getInstance.invoke(jarFactoryClazz);
// JarFile jarFile = jarFactory.get(url);
Method get = jarFactoryClazz.getMethod("get", URL.class);
get.setAccessible(true);
Object jarFile = get.invoke(jarFactory, url);
// jarFactory.close(jarFile);
Method close = jarFactoryClazz.getMethod("close", JarFile.class);
close.setAccessible(true);
//noinspection JavaReflectionInvocation
close.invoke(jarFactory, jarFile);
// jarFile.close();
((JarFile) jarFile).close();
}
}
// and in your code:
// i is now closed, so we should be good to delete the jar
JarBridge.closeJar(j);
System.out.println(f.delete()); // says true, phew.
Please note: All this is based on Java 8 codebase (1.8.0_144); they may not work with other / later versions.

Categories