I'm just curious (but not willing to try on my computer) what would happen if I run this code on Java?
private static void deleteAll(File file)
{
for(File f : file.listFiles())
{
if(f.isFile())
{
f.delete();
}
else
{
deleteAll(f);
f.delete();
}
}
}
public static void main(String[] args) {
File file = new File("/home");
deleteAll(file);
}
Is this equivalent to rm -rf / in linux and will delete everything on my computer? Or will the operating system stop me from deleting System files?
Thank you so much!
Okay this got my interest and I tried it on a lxc container. Now it wasn't a scientific test, but better than nothing right.
Well, I would say that I got mixed results. If you try to delete files with java normally, not system ones, then you are gonna do your work. Once you start killing your system, then thnings change. Java won't allow you to delete anything from your root directory, but I did successfuly delete my user folder (/home/ubuntusuer). I didn't corrupt the system, alas.
There is a mechanism to stop you from deleting a whole system. So the behaviour will not be exactly the same as compared to rm -rf /, but you can still remove a lot of stuff with it.
Related
I have a java application that write files on given location.
Before writing I am checking if provided path is writable by using Files.isWritable(path).
When user is giving the path of folder that is present in network shared drive.
Then getting AccessDeniedException.
Don't know whats happening, am I missing something. Because I am able to create/edit/delete files and folders on shared drive.
But with java application unable to do so.
While searching for some answer found this
https://bugs.openjdk.java.net/browse/JDK-8034057
Don't know if its related to my issue.
Can anyone help me with this issue and tell me whats going wrong.
Have also checked with network team. I have full read write permission on shared drive.
Java version 1.8.0_191
OS- Win 7 enterprise
This is my code for checking
private static boolean isWritable(Path path)
{
if(Files.isWritable(path))
{
return true;
}
else
{
try (FileChannel fc = FileChannel.open(path, StandardOpenOption.WRITE))
{
// NOP
}
catch(IOException e)
{
if(LOGGER.isDebugEnabled())
{
LOGGER.debug("", e);
}
return false;
}
return true;
}
}
Attached the image. So basically I cannot create file in any of the 4 shared drive i.e. I/O/R/S drives But I am able to create in C/D drive. Manually I am able to but with java program I am not.
This question already has an answer here:
Java7 WatchService - Access Denied error trying to delete recursively watched nested directories (Windows only)
(1 answer)
Closed 7 years ago.
I'm using the new feature of Java7 for watching directories for changes (as it is described in the following tutorial: http://download.oracle.com/javase/tutorial/essential/io/notification.html )
The watching itself works (nearly) without problems.
More or less I have two issues and I hope some of you have an idea how to handle it.
After a folder is added to watch, the VM has access to it and it doesn't release it. This should not be such a big issue. But if you're creating a directory in another directory and in this one more. (e.g. you create in directory c:\tmp\a the directory b and in b the directory c (c:\tmp\a\b\c)) it is not possible to delete the directory a, because of the access of the VM to b and c. If you want to delete all of the folders (e.g., with your Windows Explorer) you have to delete c first, then b and then a. It's strange, but this way works (though is inconvenient)
This issue seems to be a result of the first one.
If many changes in a occur sometimes (more or less) I get an exception which tells me, the newly created folder is used by another process and it is not possible to access it. It's strange, because I think this should not matter to get a notification. And because it is not completely clear when exactly this exception is thrown.
Do you have an idea how to make it possible to not have the lock or at least to let the user the ability to delete such a directory structure in the classical convenient way?
Well I've tried the code showcased in the link you posted, doing the same things you're trying to do:
In my c:\temp directory I create programatically a c:\temp\a dir and then a c:\temp\a\b directory:
File startDir = new File("c:\temp");
if(!startDir.exists()) {
startDir.mkdir();
}
File aDir = new File("c:\\temp\\a");
File bDir = new File("c:\\temp\\a\\b");
if(!aDir.exists()) {
aDir.mkdir();
}
if(!bDir.exists()) {
bDir.mkdir();
}
Then I add watchers to the "a" and "b" directories:
public static void watch(final File dir,final WatchService watcher) {
Path path = dir.toPath();
try {
final WatchKey bDirWatchKey = path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
new Thread(new Runnable() {
public void run() {
System.out.println("Watching: "+dir.getName());
while(true) {
try {Thread.sleep(1000);} catch (InterruptedException e) {}
List<WatchEvent<?>> events = bDirWatchKey.pollEvents();
for(WatchEvent<?> event:events) {
System.out.println(dir.getName()+" event: #"+event.count()+","+event.kind()+" File="+event.context());
}
}
}
}).start();
} catch (IOException x) {
x.printStackTrace();
}
}
This works ok, if I modify files in "a" or "b" I get the corresponding console output.
It's true that with Windows Explorer (on a Windowx XP machine) I cannot delete a watched directory (it tells me I don't have access rights). I can however delete it with other tools such as Total Commander. I can even delete then from the Windows command line with rd c:\temp\a\b. I think this is more of an issue with Windows Explorer than with Java...
with regard to your 2nd problem, if you are getting the exception " the newly created folder is used by another process and it is not possible to access it". You can try using Atomic Operations in your code. this will help the operations to be not interrupted and MAY solve your problem.
Why do I get this error when I run this program? This occurs after random iterations. Usually after the 8000th iteration.
public static void main(String[] args)
{
FileWriter writer = null;
try
{
for(int i = 0; i < 10000; i++)
{
File file = new File("C:\\Users\\varun.achar\\Desktop\\TODO.txt");
if(file.exists())
{
System.out.println("File exists");
}
writer = new FileWriter(file, true);
writer.write(i);
System.out.println(i);
writer.close();
if(!file.delete())
{
System.out.println("unable to delete");
}
//Thread.sleep(10);
//writer = null;
//System.gc();
}
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
if(writer != null)
{
try
{
writer.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
After the exception occurs, the file isn't present. That means the it is deleting, but FIleWriter tries to acquire the lock before that, even though it isn't a multi threaded program. Is it because the Windows isn't deleting the file fast enough, and hence the FileWriter doesn't get a lock? If so, then file.delete() method returns before windows actually deletes it?
How do i resolve it, since i'm getting a similar issue during load testing my application.
EDIT 1: Stacktrace:
java.io.FileNotFoundException: C:\Users\varun.achar\Desktop\TODO.txt (Access is denied)
at java.io.FileOutputStream.openAppend(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:192)
at java.io.FileOutputStream.<init>(FileOutputStream.java:116)
at java.io.FileWriter.<init>(FileWriter.java:61)
EDIT 2 : Added file.exists() and file.delete conditions in the program. and the new stacktrace:
7452
java.io.FileNotFoundException: C:\Users\varun.achar\Desktop\TODO.txt (Access is denied)
at java.io.FileOutputStream.openAppend(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:192)
at java.io.FileWriter.<init>(FileWriter.java:90)
at com.TestClass.main(TestClass.java:25)
EDIT 3 Thread dump
TestClass [Java Application]
com.TestClass at localhost:57843
Thread [main] (Suspended (exception FileNotFoundException))
FileOutputStream.<init>(File, boolean) line: 192
FileWriter.<init>(File, boolean) line: 90
TestClass.main(String[]) line: 24
C:\Users\varun.achar\Documents\Softwares\Java JDK\JDK 6.26\jdk\jre\bin\javaw.exe (09-Nov-2011 11:57:34 PM)
EDIT 4 : Program runs successfully on different machine with same OS. Now how do i ensure that the app with run successfully in the machine it is deployed in?
On any OS you can have only a certain number of open files/threads at a stretch. You seem to be hitting your OS limit. Try setting file to null inside the loop.
If I understand your stack trace correctly, the exception is coming when trying to create a new FileWriter. It's impossible to know the reason without investigating a bit further.
Return values may tell something. Especially, check what File.delete() returns.
Before trying to create new FileWriter, check what File.exists() returns.
If the previous delete() returns true and the exists() right after it also returns true, in a single-threaded program, then it's indeed something weird.
Edit: so it seems that deletion was successful and the file didn't exist after that. That how it's supposed to work, of course, so it's weird why FileWriter throws the exception. One more thought, try checking File.getParentFile().canWrite(). That is, do your permissions to write to the directory somehow disappear.
Edit 2:
Don't get the error on a different machine with the same OS. Now how do i make sure that this error won't come in the app where it'll be deployed?
So far you have one machine that works incorrectly and one that works correctly. Maybe you could try it on even more machines. It's possible that the first machine is somehow broken and that causes errors. It's amazing how often digital computers and their programs (I mean the OS and Java, not necessarily your program) can be just a "little bit broken" so that they work almost perfectly almost all of the time, but fail randomly with some specific hardware & use case - usually under heavy load - similar to how incorrect multi-threaded programs can behave. It doesn't have to be your fault to be your problem :-)
Frankly, the only way to make sure that errors won't come up in machine X is to run the program on machine X. Unusual stuff such as creating and deleting the same file 8000 times in rapid succession is prone to errors, even though it "should" work. Computers, operating systems and APIs are not perfect. The more unusual stuff you do, the more often the imperfections will realize themselves, because unusual usage is generally less thoroughly tested than everyday operations.
I have had the same issue, a java program (single threaded) that opens, deleted then re-opens the same file continuously.
On some windows systems we get the same issue as reported here, on Linux, Solaris, and various other windows systems it works fine.
Traceing the program with SysInternals Process Monitor (now MS) its clear the delete is done first, at the OS level, and clear the subsequent open fails with PENDING DELETE status.
So there seems to be some slight delay at the OS/NTFS/Disk level before the file is actually deleted, and that seems to be the cause of the random failure in our case.
As a workaround, I changed the .delete() call to instead just write over the top of it new FileWriter(file) and that seems to be working.
The problem did not occur on all systems, one specific model that would always fail, all be it after not fixed number of loops, was a Windows 7 / Dell Lattitude E6420 with WD Smartdrive, whereas my Windows 7 / Dell precision M4600 (with solid state drive) or T3400 with Linux I have never had the issue.
Cheers - Mark
It may be a long shot, but, can you try to work with a file that is NOT directly sitting on the Desktop. Instead of:
"C:\\Users\\varun.achar\\Desktop\\TODO.txt"
Try:
"C:\\Users\\varun.achar\\SomeOtherDirectory\\TODO.txt"
OS may be killing you here with all the Desktop hooks...
EDIT based on the comments:
Are there any scheduled jobs running on the "bad" machine?
Instead of debugging the environment, do you have a sys admin to do that?
Does this work on a clean Windows install? [95% chance it will]
Since the root cause seems to be environment, instead of solving a Windows configuration problem, would you be able to move forward with other tasks, and leave it to someone who keeps the list of discrepancies between the systems?
Can you conditionally try to write to the file ?
Using file.exists and then writing to it, so you can potentially avoid any other issues. Hard to say from this exception.
http://download.oracle.com/javase/6/docs/api/java/io/File.html#exists()
Could you also post a thread dump at that point, just to debug it further.
Please flush the writer, before writing again.
These are the scenerios you should handle before deleting a file http://www.java2s.com/Code/Java/File-Input-Output/DeletefileusingJavaIOAPI.htm
at least check for return value in your program.
Thanks folks for help me out but this is how it got resolved finally.
public static void main(String[] args)
{
FileWriter writer = null;
try
{
for(int i = 0; i < 10000; i++)
{
File file = new File("C:\\tenant-system-data\\abc.txt");
if(!file.getParentFile().canWrite())
{
System.out.println("parent file error");
}
if(file.exists())
{
System.out.println("File exists");
}
int count = 0;
while(count++ < 5)
{
try
{
file.createNewFile();
break;
}
catch(IOException e)
{
try
{
Thread.sleep(100);
}
catch(InterruptedException e1)
{
e1.printStackTrace();
}
}
}
writer = new FileWriter(file, true);
writer.write(i);
System.out.println(i);
writer.close();
if(!file.delete())
{
System.out.println("unable to delete");
}
//Thread.sleep(10);
//writer = null;
//System.gc();
}
}
catch(IOException e)
{
e.printStackTrace();
}
finally
{
if(writer != null)
{
try
{
writer.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
I just had the same problem (FileWriter & Access Denied).
My guess for the reason: Windows 7 had put a lock on the file because a preview of the file content was shown in an Explorer window (the file was selected in the window).
Solution: I de-selected the file in the Explorer window. And the IOException was gone.
You have delete permission in the directory but not create permission.
I know. Heresy. But I'm in a bind. I have a lot of config files that use absolute path names, which creates an incompatibility between OS X and Windows. If I can get OS X (which I'm betting is the more flexible of the two) to recognize Q:/foo/bar/bim.properties as a valid absolute file name, it'll save me days of work spelunking through stack traces and config files.
In the end, I need this bit of Java test code to print "SUCCESS!" when it runs:
import java.io.*;
class DriveLetterTest {
static public void main(String... args) {
File f = new File("S:");
if (f.isDirectory()) {
System.out.println("SUCCESS!");
} else {
System.out.println("FAIL!");
}
}
}
Anyone know how this can be done?
UPDATE: Thanks for all the feedback, everyone. It's now obvious to me I really should have been clearer in my question.
Both the config files and the code that uses them belong to a third-party package I cannot change. (Well, I can change them, but that means incurring an ongoing maintenance load, which I want to avoid if at all possible.)
I'm in complete agreement with all of you who are appalled by this state of affairs. But the fact remains: I can't change the third-party code, and I really want to avoid forking the config files.
Short answer: No.
Long answer: For Java you should use System.getProperties(XXX).
Then you can load a Properties file or Configuration based on what you find in os.name.
Alternate Solution just strip off the S: when you read the existing configuration files on non-Windows machines and replace them with the appropriate things.
Opinion: Personally I would bite the bullet and deal with the technical debt now, fix all the configuration files at build time when the deployment for OSX is built and be done with it.
public class WhichOS
{
public static void main(final String[] args)
{
System.out.format("System.getProperty(\"os.name\") = %s\n", System.getProperty("os.name"));
System.out.format("System.getProperty(\"os.arch\") = %s\n", System.getProperty("os.arch"));
System.out.format("System.getProperty(\"os.version\") = %s\n", System.getProperty("os.version"));
}
}
the output on my iMac is:
System.getProperty("os.name") = Mac OS X
System.getProperty("os.arch") = x86_64
System.getProperty("os.version") = 10.6.4
Honestly, don't hard-code absolute paths in a program, even for a single-platform app. Do the correct thing.
The following is my wrong solution, saved to remind myself not to repeat giving a misdirected advice ... shame on me.
Just create a symbolic link named Q: just at the root directory / to / itself.
$ cd /
$ ln -s / Q:
$ ln -s / S:
You might need to use sudo. Then, at the start of your program, just chdir to /.
If you don't want Q: and S: to show up in the Finder, perform
$ /Developer/Tools/SetFile -P -a V Q:
$ /Developer/Tools/SetFile -P -a V S:
which set the invisible-to-the-Finder bit of the files.
The only way you can replace java.io.File is to replace that class in rt.jar.
I don't recommend that, but the best way to do this is to grab a bsd-port of the OpenJDK code, make necessary changes, build it and redistribute the binary with your project. Write a shell script to use your own java binary and not the built-in one.
PS. Just change your config files! Practice your regex skills and save yourself a lot of time.
If you are not willing to change your config file per OS, what are they for in first place?
Every installation should have its own set of config files and use it accordingly.
But if you insist.. you just have to detect the OS version and if is not Windows, ignore the letter:
Something along the lines:
boolean isWindows = System.getProperty("os.name").toLowerCase()
.contains("windows");
String folder = "S:";
if (isWindows && folder.matches("\\w:")) {
folder = "/";
} else if (isWindows && folder.matches("\\w:.+")) {
folder = folder.substring(2);// ignoring the first two letters S:
}
You get the idea
Most likely you'd have to provide a different java.io.File implementation that can parse out the file paths correctly, maybe there's one someone already made.
The real solution is to put this kind of stuff (hard-coded file paths) in configuration files and not in the source code.
Just tested something out, and discovered something interesting: In Windows, if the current directory is on the same logical volume (i.e. root is the same drive letter), you can leave off the drive letter when using a path. So you could just trim off all those drive letters and colons and you should be fine as long as you aren't using paths to items on different disks.
Here's what I finally ended up doing:
I downloaded the source code for the java.io package, and tweaked the code for java.io.File to look for path names that start with a letter and a colon. If it finds one, it prepends "/Volumes/" to the path name, coughs a warning into System.err, then continues as normal.
I've added symlinks under /Volumes to the "drives" I need mapped, so I have:
/Volumes/S:
/Volumes/Q:
I put it into its own jar, and put that jar at the front of the classpath for this project only. This way, the hack affects only me, and only this project.
Net result: java.io.File sees a path like "S:/bling.properties", and then checks the OS. If the OS is OS X, it prepends "/Volumes/", and looks for a file in /Volumes/S:/bling.properties, which is fine, because it can just follow the symlink.
Yeah, it's ugly as hell. But it gets the job done for today.
I want to delete the old log files in log directory.
To delete the log files which are more than 6 months,
I have written the script like
find /dnbusr1/ghmil/BDELogs/import -type f -mtime +120 -exec rm -f {} \;
By using this script i am able to delete the old files, but how do I invoke this script by using java?
Use java.lang.Runtime.exec() .
If portability is an issue, preventing you from running with Runtime.exec(), this code is quite trivial to write in Java using File and a FilenameFilter.
Edit: Here is a static method to delete a directory tree... you can add in the filtering logic easily enough:
static public int deleteDirectory(File dir, boolean slf) {
File[] dc; // directory contents
String dp; // directory path
int oc=0; // object count
if(dir.exists()) {
dir=dir.getAbsoluteFile();
if(!dir.canWrite()) {
throw new IoEscape(IoEscape.NOTAUT,"Not authorized to delete directory '"+dir+"'.");
}
dp=dir.getPath();
if(dp.equals("/") || dp.equals("\\") || (dp.length()==3 && dp.charAt(1)==':' && (dp.charAt(2)=='/' || dp.charAt(2)=='\\'))) {
// Prevent deletion of the root directory just as a basic restriction
throw new IoEscape(IoEscape.GENERAL,"Cannot delete root directory '"+dp+"' using IoUtil.deleteDirectory().");
}
if((dc=dir.listFiles())!=null) {
for(int xa=0; xa<dc.length; xa++) {
if(dc[xa].isDirectory()) {
oc+=deleteDirectory(dc[xa]);
if(!dc[xa].delete()) { throw new IoEscape(IoEscape.GENERAL,"Unable to delete directory '"+dc[xa]+"' - it may not be empty, may be in use as a current directory, or may have restricted access."); }
}
else {
if(!dc[xa].delete()) { throw new IoEscape(IoEscape.GENERAL,"Unable to delete file '"+dc[xa]+"' - it may be currently in use by a program, or have restricted access."); }
}
oc++;
}
}
if(slf) {
if(!dir.delete()) { throw new IoEscape(IoEscape.GENERAL,"Unable to delete directory '"+dir+"' - it may not be empty, may be in use as a current directory, or may have restricted access."); }
oc++;
}
}
return oc;
}
When you only want to call the command you described call:
Runtime r = Runtime.getRuntime();
Process process = r.exec("find /dnbusr1/ghmil/BDELogs/import -type f -mtime +120 -exec rm -f {} \\;"); //$NON-NLS-1$
process.waitFor();
If you want to call more than one command use Chris Jester-Young answer.
The exec method can also define files you want to use. The other answers link the method describtion.
Using system calls in Java is possible, but is generally a bad idea because you'll lose the portability of the code. You could look into Ant and use something like this purge task.
Adding to Crashworks's answer, you call with these arguments in the cmdarray:
new String[] {"find", "/dnbusr1/ghmil/BDELogs/import", "-type", "f",
"-mtime", "+120", "-exec", "rm", "-f", "{}", ";"}
If your find supports the -exec ... {} + syntax, change the ";" at the end to "+". It will make your command run faster (it will call rm on a batch of files at once, rather than once for each file).