I'm trying to write code for a jarfile which if executed, it shutdowns the JVM and then deletes the jarfile. This is what I've tried to do so far but it is not deleting the file after the JVM closes.
public static void check() {
if (isJarFile()) {
try (Scanner s = new Scanner(new URL(HASH_PROVIDER).openStream())) {
String remote_hash = s.nextLine().trim();
File jarFile = getJarFile();
if (jarFile != null && !remote_hash.equals(getMD5Checksum(jarFile.getAbsolutePath()))) {
jarFile.setWritable(true);
jarFile.deleteOnExit();
}
System.exit(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
}
public static byte[] createChecksum(String filename) throws Exception {
InputStream fis = new FileInputStream(filename);
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
public static String getMD5Checksum(String filename) throws Exception {
byte[] b = createChecksum(filename);
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
public static File getJarFile() {
try {
return new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
Can someone explain why deleteOnExit is not working in this instance?
Make sure that you close any stream that you have open on a file before exiting your JVM. Otherwise, the shut down hook that is supposed to delete the file cannot trigger on Windows as opening the stream triggers a file lock on the OS-level.
For your example, it means that you must not end the JVM process before exiting the try-with-ressources-block which is roughly translated into:
Scanner s = new Scanner(new URL(HASH_PROVIDER).openStream())
try {
// your code
System.exit(0);
} finally {
s.close(); // Never executed
}
As your program exits before the finally block is executed, the shut down hook is triggered without closing the stream and the file cannot be deleted.
Note that the following code will work for your purposes as the finally block is executed after closing the try-with-ressources argument:
try (Scanner s = new Scanner(new URL(HASH_PROVIDER).openStream())) {
// your code
} catch (Exception e) {
e.printStackTrace();
} finally {
System.exit(0);
}
Related
I use PrintWriter to write file with autoFlush = true:
PrintWriter pw = new PrintWriter(new FileOutputStream(file), autoFlush);
Without calling pw.flush(), every time I call pw.println(...), content is write to output file.
It work fine on Win7 and Win Server 2008 but not Win Server 2012. I tried using same JDK, for Win Server 2012 env, file only flush out after pw.close() is called in finally block (e.g reach the end of program or exception happen).
According to javadoc: https://docs.oracle.com/javase/7/docs/api/java/io/Writer.html#flush()
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.
Seem like OS issue. Any help on that?
Here the code:
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class TestPW {
public static void main(String[] args) throws Exception {
File file = null;
PrintWriter pw = null;
try {
boolean autoFlush = true;
file = new File("C:\\NotBackedUp\\test.txt");
pw = new PrintWriter(new FileOutputStream(file), autoFlush);
int loop = 100000000;
while (loop > 0) {
pw.println("Test: " + loop);
loop--;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (pw != null) {
pw.close();
}
} catch (Exception e) {
}
}
}
}
The workaround is close the pw after some number of loop processed and re-init the pw object again as below....
import java.io.*;
public class TestPSMaxLine {
public static void main(String[] args) throws Exception {
File file = null;
PrintStream ps = null;
int maxLinesToWriteBeforeClose = 100000;
try {
boolean appendFile = true;
boolean autoFlush = true;
file = new File("C:\\NotBackedUp\\test.txt");
ps = new PrintStream(new FileOutputStream(file, appendFile), autoFlush);
int loop = 100000000;
int lineNum = 0;
while (loop > 0) {
ps.println("Test: " + loop);
loop--;
lineNum++;
if(lineNum % maxLinesToWriteBeforeClose ==0){
closeFile(ps);
ps = new PrintStream(new FileOutputStream(file, appendFile), autoFlush);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeFile(ps);
}
}
private static void closeFile(PrintStream ps){
try{
if (ps != null) {
ps.close();
}
} catch (Exception e) {
}
}
}
There was an exception, but PrintWriter swallows exceptions. You need to check for them manually. Or else use BufferedWriter, which doesn't swallow exceptions.
Recently I added "adb devices" in the nano ./bash_profile so that I can run it from any directory.
I used one java application to run
public static void main(String [] args) {
executeCmd("adb devices");
}
private static void executeCmd(String string) {
InputStream pipedOut = null;
try {
Process aProcess = Runtime.getRuntime().exec(string);
// These two thread shall stop by themself when the process end
Thread pipeThread = new Thread(new StreamGobber(aProcess.getInputStream()));
Thread errorThread = new Thread(new StreamGobber(aProcess.getErrorStream()));
pipeThread.start();
errorThread.start();
aProcess.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
class StreamGobber implements Runnable {
private InputStream Pipe;
public StreamGobber(InputStream pipe) {
if(pipe == null) {
throw new NullPointerException("bad pipe");
}
Pipe = pipe;
}
public void run() {
try {
byte buffer[] = new byte[2048];
int read = Pipe.read(buffer);
while(read >= 0) {
System.out.write(buffer, 0, read);
read = Pipe.read(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(Pipe != null) {
try {
Pipe.close();
} catch (IOException e) {
}
}
}
when I run any other commands such as "ls" it's working fine!!
I'm using mac ..
thanks :)
Maybe global path problem on mac. You can try run with absolute adb program path as command.
Okay, this is going to be a bit long. So I made a junit test class to test my program. I wanted to test if a method that uses a Scanner to read a file into the program threw and exception, if the file didn't exist like this:
#Test
public void testLoadAsTextFileNotFound()
{
File fileToDelete = new File("StoredWebPage.txt");
if(fileToDelete.delete()==false) {
System.out.println("testLoadAsTextFileNotFound - failed");
fail("Could not delete file");
}
try{
assertTrue(tester.loadAsText() == 1);
System.out.println("testLoadAsTextFileNotFound - passed");
} catch(AssertionError e) {
System.out.println("testLoadAsTextFileNotFound - failed");
fail("Did not catch Exception");
}
}
But the test fails at "could not delete file", so I did some searching. The path is correct, I have permissions to the file because the program made it in the first place. So the only other option would be, that a stream to or from the file is still running. So I checked the method, and the other method that uses the file, and as far as I can, both streams are closed inside the methods.
protected String storedSite; //an instance variable
/**
* Store the instance variable as text in a file
*/
public void storeAsText()
{
PrintStream fileOut = null;
try{
File file = new File("StoredWebPage.txt");
if (!file.exists()) {
file.createNewFile();
}
fileOut = new PrintStream("StoredWebPage.txt");
fileOut.print(storedSite);
fileOut.flush();
fileOut.close();
} catch(Exception e) {
if(e instanceof FileNotFoundException) {
System.out.println("File not found");
}
fileOut.close();
} finally {
if(fileOut != null)
fileOut.close();
}
}
/**
* Loads the file into the program
*/
public int loadAsText()
{
storedSite = ""; //cleansing storedSite before new webpage is stored
Scanner fileLoader = null;
try {
fileLoader = new Scanner(new File("StoredWebPage.txt"));
String inputLine;
while((inputLine = fileLoader.nextLine()) != null)
storedSite = storedSite+inputLine;
fileLoader.close();
} catch(Exception e) {
if(e instanceof FileNotFoundException) {
System.out.println("File not found");
return 1;
}
System.out.println("an Exception was caught");
fileLoader.close();
} finally {
if(fileLoader!=null)
fileLoader.close();
}
return 0; //return value is for testing purposes only
}
I'm out of ideas. Why can't I delete my file?
EDIT: i've edited the code, but still this give me the same problem :S
You have two problems here. The first is that if an exception is thrown during your write to the file, the output stream is not closed (same for the read):
try {
OutputStream someOutput = /* a new stream */;
/* write */
someOutput.close();
The second problem is that if there's an exception you aren't notified:
} catch (Exception e) {
if (e instanceof FileNotFoundException) {
/* do something */
}
/* else eat it */
}
So the problem is almost certainly that some other exception is being thrown and you don't know about it.
The 'correct' idiom to close a stream is the following:
OutputStream someOutput = null;
try {
someOutput = /* a new stream */;
/* write */
} catch (Exception e) {
/* and do something with ALL exceptions */
} finally {
if (someOutput != null) someOutput.close();
}
Or in Java 7 you can use try-with-resources.
I am wondering why I get this warning with the new eclipse Juno despite I think I correctly closed everything. Could you please tell me why I get this warning in the following piece of code?
public static boolean copyFile(String fileSource, String fileDestination)
{
try
{
// Create channel on the source (the line below generates a warning unassigned closeable value)
FileChannel srcChannel = new FileInputStream(fileSource).getChannel();
// Create channel on the destination (the line below generates a warning unassigned closeable value)
FileChannel dstChannel = new FileOutputStream(fileDestination).getChannel();
// Copy file contents from source to destination
dstChannel.transferFrom(srcChannel, 0, srcChannel.size());
// Close the channels
srcChannel.close();
dstChannel.close();
return true;
}
catch (IOException e)
{
return false;
}
}
IF you're running on Java 7, you can use the new try-with-resources blocks like so, and your streams will be automatically closed:
public static boolean copyFile(String fileSource, String fileDestination)
{
try(
FileInputStream srcStream = new FileInputStream(fileSource);
FileOutputStream dstStream = new FileOutputStream(fileDestination) )
{
dstStream.getChannel().transferFrom(srcStream.getChannel(), 0, srcStream.getChannel().size());
return true;
}
catch (IOException e)
{
return false;
}
}
You won't need to explicitly close the underlying channels. However if you're not using Java 7, you should write the code in a cumbersome old way, with finally blocks:
public static boolean copyFile(String fileSource, String fileDestination)
{
FileInputStream srcStream=null;
FileOutputStream dstStream=null;
try {
srcStream = new FileInputStream(fileSource);
dstStream = new FileOutputStream(fileDestination)
dstStream.getChannel().transferFrom(srcStream.getChannel(), 0, srcStream.getChannel().size());
return true;
}
catch (IOException e)
{
return false;
} finally {
try { srcStream.close(); } catch (Exception e) {}
try { dstStream.close(); } catch (Exception e) {}
}
}
See how much better the Java 7 version is :)
You should always close in finally because if an exception rise, you won't close the resources.
FileChannel srcChannel = null
try {
srcChannel = xxx;
} finally {
if (srcChannel != null) {
srcChannel.close();
}
}
Note: even if you put a return in the catch block, the finally block will be done.
eclipse is warning you about the FileInputStream and FileOutputStream that you can no longer reference.
Under what circumstances read end can be dead in couple PipedOutputStream and PipedInputStream? I am not closing any pipes.
I encountered java.io.IOException: Read end dead in my code and found out the cause. Posting an example code below. You will get an "Read end dead" exception if you run the code. If you take a close look, the consumer thread reads "hello" from the stream and terminates; meanwhile the producer sleeps for 2 seconds and tries to write " world" but fails. A related problem explained here: http://techtavern.wordpress.com/2008/07/16/whats-this-ioexception-write-end-dead/
class ReadEnd {
public static void main(String[] args) {
final PipedInputStream in = new PipedInputStream();
new Thread(new Runnable() { //consumer
#Override
public void run() {
try {
byte[] tmp = new byte[1024];
while (in.available() > 0) { // only once...
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
System.out.print(new String(tmp, 0, i));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}).start();
PipedOutputStream out = null;
try {
out = new PipedOutputStream(in);
out.write("hello".getBytes());
Thread.sleep(2 * 1000);
out.write(" world".getBytes()); //Exception thrown here
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
}
}