I use IntelliJ. I have a class that manages student grades. It can edit files, which I have a new .temp file being written, and renamed. Then the old file gets deleted. On my laptop (mac) this works fine, but on my desktop (windows) everything works, but the old file is not deleted, and temp is not renamed.
Below is my method to edit the files:
private static void editStuGrade() throws Exception {
System.out.println("Enter a course grade you want to change (q1,q2,q3,mid,final): ");
String editInput = command.next();
System.out.println("Enter a new score: ");
String newGrade = command.next();
Path p = Paths.get(System.getProperty("user.dir"),"src","Assignment1", student.name + ".txt");
File inputFile = new File(p.toString());
FileInputStream inputStream = new FileInputStream(inputFile);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(inputStreamReader);
File outputFile = new File(p + ".temp");
FileOutputStream outputStream = new FileOutputStream(outputFile);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter writer = new BufferedWriter(outputStreamWriter);
if (editInput.equalsIgnoreCase("q1")) {
writer.write(student.name + "," + student.id + "," + newGrade + "," + student.quiz2
+ "," + student.quiz3 + "," + student.midterm + "," + student.finalTest);
writer.close();
} else if (editInput.equalsIgnoreCase("q2")) {
writer.write(student.name + "," + student.id + "," + student.quiz1 + "," + newGrade
+ "," + student.quiz3 + "," + student.midterm + "," + student.finalTest);
writer.close();
} else if (editInput.equalsIgnoreCase("q3")) {
writer.write(student.name + "," + student.id + "," + student.quiz1 + "," + student.quiz2
+ "," + newGrade + "," + student.midterm + "," + student.finalTest);
writer.close();
} else if (editInput.equalsIgnoreCase("mid")) {
writer.write(student.name + "," + student.id + "," + student.quiz1 + "," + student.quiz2
+ "," + student.quiz3 + "," + newGrade + "," + student.finalTest);
writer.close();
} else if (editInput.equalsIgnoreCase("final")) {
writer.write(student.name + "," + student.id + "," + student.quiz1 + "," + student.quiz2
+ "," + student.quiz3 + "," + student.midterm + "," + newGrade);
writer.close();
}
inputFile.delete();
outputFile.renameTo(new File(p.toString()));
System.out.println("Successful.");
}
Windows is a special snowflake. Unlike other OSes (or rather, Windows File System, unlike other file systems) does not let you delete any open files, and does not let you rename or delete directories of they contain any open files. In contrast to other OSes which don't mind at all; files on disk are merely 'pointers', and any process that opens a file also gets a pointer. The file isn't truly removed from disk until all pointers are gone, so, you can just delete files - if that file is still open, no problem - as long as it is the file doesn't disappear. It's very similar to java garbage collection in that way.
But not so on windows.
Your code has a bug in it - you aren't managing your resources. This is resulting in the files being open, and then you try to delete them - this works on non-windows filesystems but isn't allowed on windows - you can't delete files even if you're the very process that still has them open.
Resources MUST be closed, and the responsibility to do this lies on you. Given that code can exit in many ways (not just 'by running to the end of a method', but also: With return, by throwing an exception, by using break or continue for flow control, etc). Therefore, trying to write code by hand that ensures your resource is closed for all possible exit paths is annoying and error prone, so, don't. Use java's language features:
Do not EVER open a resource unless you do so in a try-with block.
Looks like this:
try (var outputStream = new FileOutputStream(outputFile)) {
// outputStream exists here and can be interacted with as normal
}
No matter how code flow 'exits' that try block, the resource is closed automatically once it does. This is good - not just because this lets you delete those files, but also because every process gets a limited number of files they get to open, so if you fail to close, any non-trivial app will soon hard-crash due to having too many open files.
What are resources? The javadoc will tell you, and use common sense. most InputStream and OutputStreams are - any type that implements AutoClosable tends to be. If you new X() them up you definitely have to close them. If you're invoking a method that sounds like it 'makes' the resource (example: socket.getInputStream or Files.newInputStream), you have to close them.
Use try () {} to do this.
Once you do so, you can delete these files just fine, even on windows.
Related
I am trying to use a text file as a database and I came across a problem. The problem is when I write the user info on the text file, it doesn't write to the next line when I add a second user. It just overwrites the old one. How can I add a writeToNextLine kind of thing?
public void addAdmin(String adminName, String adminSurName, String adminUserName, String adminPassword) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\cemon\\IdeaProjects\\Library\\src\\database\\AdminUserData.txt"));
writer.write(adminName + " " + adminSurName);
writer.newLine();
writer.write(adminUserName + " " + adminPassword);
writer.newLine();
writer.close();
}
Bad idea. How do you know you're not adding a duplicate user?
If you want a simple text file as a database, load the text file into memory on startup, e.g. as a Map<String, User> keyed by adminUserName, and add a new user there. Whenever a value changes, write all the users back to the text file.
That also ensures that (future) operations like "remove user" will work correctly.
Recommend implementing a UserStore class with the suggested Map as a field, to keep the implementation hidden (encapsulated) from the rest of your program.
You want to enable append mode in the FileWriter. This means that when you write a new line, instead of overwriting, it adds another line.
Your code should be:
public void addAdmin(String adminName, String adminSurName, String adminUserName, String adminPassword) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\cemon\\IdeaProjects\\Library\\src\\database\\AdminUserData.txt", true));
writer.write(adminName + " " + adminSurName);
writer.newLine();
writer.write(adminUserName + " " + adminPassword);
writer.newLine();
writer.close();
}
I have several folders of size >2.5GB on C drive which is SSD. Through Java, I'm moving these folders to another shared drive which also happens to SSD using FileUtils.copyDirectoryToDirectory(sourceDir, destiDir);
It works fine but is slow (taking ~30 mins) when compared to windows default move option which takes 5 mins. I googled around to see if there is a better way to increase the performance of moving directories through my java program but no luck. Can someone suggest me the best way to move these directories?
ok this is what I did
Used a robocopy command within java to copy directories between two locations. Tested with a ~9GB file and was able to copy in ~9 mins. Below is the code snippet
String sourceFolder = new File("C:\\test\\robocopytest\\source\\20170925T213857460").toString();
String destFolder = new File("C:\\test\\robocopytest\\destination\\20170925T213857460").toString();
StringBuffer rbCmd = new StringBuffer();
if ((sourceFolder != null) && (destFolder != null))
{
if (sourceFolder.contains(" ")) {
if (sourceFolder.startsWith("\\")) {
sourceFolder = "/\"" + sourceFolder.substring(1) + "/\"";
} else {
sourceFolder = "\"" + sourceFolder + "\"";
}
}
if (destFolder.contains(" ")) {
if (destFolder.startsWith("\\")) {
destFolder = "/\"" + destFolder.substring(1) + "/\"";
} else {
destFolder = "\"" + destFolder + "\"";
}
}
rbCmd.append("robocopy " + sourceFolder + " " + destFolder);
Process p = Runtime.getRuntime().exec(rbCmd.toString());
}
I am quite confused here. I am preparing to SCJP, I wrote a small recursive to check BufferedWriter memory allocations before|after flush() & close() methods. Please see below code block, I have just wrote my Desktop files into a text file.
import java.io.*;
public class Myexception{
private static String lvl = "";
static BufferedWriter bw;
private static File source = new File("C:\\Users\\"+System.getProperty("user.name")+"\\Desktop\\New folder\\myTest.txt");
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
System.out.println("Free memory before recursive: " + rt.freeMemory());
bw = new BufferedWriter(new FileWriter(source));
checkFiles(new File("C:\\Users\\"+System.getProperty("user.name")+"\\Desktop"), 0);
System.out.println("Memory after recursive: " + rt.freeMemory());
bw.flush();
bw.close();
lvl = null;
System.out.println("Memory after clean up: " + rt.freeMemory());
}
static void checkFiles(File file, int level) throws IOException{
if(!file.exists()){
System.out.println("File doesnt exist: " + file.getName() + file.getPath());
return;
}
for(String s:file.list()){
if(new File(file.getPath() + "\\" + s).isDirectory()){
bw.newLine();
bw.write(lvl + "Directory: " + s);
lvl += " ";
checkFiles(new File(file.getPath() + "\\" + s), level+1);
}else{
bw.newLine();
bw.write(lvl + "File: " + s);
}
}
}
}
Output is fine but what I didnt understand, free memory before than flush() & close() is the same as after than flush() & close().Please see my output:
Free memory before recursive: 126150232
Memory after recursive: 104461304
Memory after clean up: 104461304
I have checked existing topics but I couldn`t find exact explanation. I was expecting I will have more free memory after bw.close().
Thank you
closing and flushing streams has nothing to do with "cleaning up memory", they make sure that data in a BufferedStream is flushed to whatever the stream is pointing to, for example the disk or network socket usually. Read the JavaDoc it doesn't ever imply that this has anything to do or effects garbage collection in anyway. You need to go back to studying before you waste your money on a worthless certification.
Memory is cleaned only after Garbage Collector has run. Until that you will not see any changes in memory consumption.
I'm using process = Runtime.getRuntime().exec(cmd,null,new File(path));
to execute some SQL in file (abz.sql)
Command is:
"sqlplus "+ context.getDatabaseUser() + "/"
+ context.getDatabasePassword() + "#"
+ context.getDatabaseHost() + ":"
+ context.getDatabasePort() + "/"
+ context.getSid() + " #"
+ "\""
+ script + "\"";
String path=context.getReleasePath()+ "/Server/DB Scripts";
It is executing that file but not getting exit. Hence I tried using:
Writer out = new OutputStreamWriter(process.getOutputStream());
out.append("commit;\r\n");
out.append("exit \r\n");
System.out.println("---------"+out);
out.close();
This it complete block that I m using:
if(context.getConnectionField()=="ORACLE")
{
String cmd=
"sqlplus "+ context.getDatabaseUser() + "/"
+ context.getDatabasePassword() + "#"
+ context.getDatabaseHost() + ":"
+ context.getDatabasePort() + "/"
+ context.getSid() + " #"
+ "\""
+ script +"\"";
String path=context.getReleasePath()+ "/Server/DB Scripts";
process = Runtime.getRuntime().exec(cmd,null,new File(path));
out = new OutputStreamWriter(process.getOutputStream());
out.append("commit;\r\n");
out.append("exit \r\n");
System.out.println("---------"+out);
out.close();
Integer result1 = null;
while (result1 == null) {
try {
result1 = process.waitFor();
}
catch (InterruptedException e) {}
}
if(process.exitValue() != 0)
return false;
return true;
}
The code shown fails to read the error stream of the Process. That might be blocking progress. ProcessBuilder was introduced in Java 1.5 and has a handy method to redirectErrorStream() - so that it is only necessary to consume a single stream.
For more general tips, read & implement all the recommendations of When Runtime.exec() won't.
I can see a few issues here. The version of 'exec' that you are using will tokenize the command string using StringTokenizer, so unusual characters in the password (like spaces) or the other parameters being substituted are accidents waiting to happen. I recommend switching to the version
Process exec(String[] cmdarray,
String[] envp,
File dir)
throws IOException
It is a bit more work to use but much more robust.
The second issue that there are all kinds of caveat about whether or not exec will run concurrently with the Java process (see http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Process.html). So you need to say which operating system you're on. If it does not run concurrently then your strategy of writing to the output stream cannot work!
The last bit of the program is written rather obscurely. I suggest ...
for (;;) {
try {
process.waitFor();
return process.exitValue() == 0;
} catch ( InterruptedException _ ) {
System.out.println( "INTERRUPTED!" ); // Debug only.
}
}
This eliminates the superfluous variable result1, eliminates the superfluous boxing and highlights a possible cause of endless looping.
Hope this helps & good luck!
I used the following code for getting the motherboard serial number. But I got the o/p Result is empty. What mistake did I make in this code?
File file = File.createTempFile("realhowto",".vbs");
file.deleteOnExit();
FileWriter fw = new java.io.FileWriter(file);
String vbs =
"Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
+ "Set colItems = objWMIService.ExecQuery _ \n"
+ " (\"Select * from Win32_BaseBoard\") \n"
+ "For Each objItem in colItems \n"
+ " Wscript.Echo objItem.SerialNumber \n"
+ " exit for ' do the first cpu only! \n"
+ "Next \n";
fw.write(vbs);
fw.close();
Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
BufferedReader input =
new BufferedReader
(new InputStreamReader(p.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
result += line;
}
if(result.equalEgnoreCase(" ") {
System.out.println("Result is empty");
} else {
System.out.println("Result :>"+result);
}
input.close();
}
I can confirm that the VBS side of this works fine on my machine; however, the output I got was:
MB-1234567890
which doesn't seem particularly unique or helpful. Still, if this is what you're after, try the following. Paste the VBS into a .vbs file and run it using cscript <myfile>.vbs:
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * from Win32_BaseBoard")
For Each objItem in colItems
Wscript.Echo objItem.SerialNumber
exit for ' do the first cpu only!
Next
If that works, it's the Java that's at fault (and I suspect it's not capturing the process output for some reason). Otherwise, it's the VBS script failing you.
There are some more hints and tips on this thread which might give you some different strategies.
Process p = Runtime.getRuntime().exec("wmic baseboard get serialnumber");
or
Process p = Runtime.getRuntime().exec("wmic /node:"HOST" bios get serialnumber");
instead of HOST , give ur hostname, which can be arrived at typing hostname in cmd prompt.