I've got a JUnit test that tests a piece of code I've been working on that uses temporary files to perform certain functions. For whatever reason, the test passes on OSX, but fails on Windows 7. To simplify things, I copied the test into a new file, and boiled it down to be as simple as possible while still demonstrating the error.
Basically, I initialize the temporary file by writing a comma separated key-value pair into the file (and then assert that it exists, which it does). Then, I attempt to replace the value of the line, based on the key. updateValueForKey() has the boolean "checkOldVal", which, if true, requires that oldVal passed in match the one in the file. This test fails on Windows, and passes on OSX whether this is true or false
Windows Java version: 1.6.0_45
OSX Java version: 1.6.0_65
The code is as follows:
public class SimpleTempFileTest {
ReadWriteLock _fileLock = null;
File _file = null;
public SimpleTempFileTest() {
}
#Test
public void simpleTempFileTest() throws Exception {
_file = File.createTempFile("testCsv", null);
_file.deleteOnExit();
_fileLock = new ReentrantReadWriteLock();
BufferedWriter writer = null;
try {
_fileLock.writeLock().lock();
writer = new BufferedWriter(new FileWriter(_file, true));
writer.append("foo,bar");
writer.newLine();
} finally {
if (writer != null) {
writer.close();
}
_fileLock.writeLock().unlock();
}
BufferedReader br = new BufferedReader(new FileReader(_file));
String line = br.readLine();
assertTrue("Unexpected value. Line=" + line, line.equals("foo,bar"));
assertTrue("Unexpected value. Line=" + line, br.readLine() == null);
br.close();
//Fails whether checkOldVal is true or false
updateValueForKey("foo", "bar", "baz", true);
br = new BufferedReader(new FileReader(_file));
line = br.readLine();
//Everything up to this point passes, but the following assertion fails
assertTrue("Unexpected value. Line=" + line, line.equals("foo,baz"));
assertTrue("Unexpected value. Line=" + line, br.readLine() == null);
br.close();
}
String updateValueForKey(String key, String oldVal, String newVal, boolean checkOldVal) throws FileNotFoundException, IOException {
BufferedReader br = null;
BufferedWriter writer = null;
File temp = null;
try {
_fileLock.writeLock().lock();
br = new BufferedReader(new FileReader(_file));
temp = File.createTempFile("csvTmp", ".tmp");
writer = new BufferedWriter(new FileWriter(temp, true));
boolean seek = true;
String line;
while ((line = br.readLine()) != null) {
if (seek) {
String[] nvp = line.split(",");
System.out.println("nvp[0]=" + nvp[0] + ", nvp[1]=" + nvp[1]);
if (nvp[0].equalsIgnoreCase(key)) {
if (nvp[1].equals(oldVal) || !checkOldVal) {
String lineToWrite = key + "," + newVal;
System.out.println("Writing " + lineToWrite);
writer.write(lineToWrite);
writer.newLine();
seek = false;
continue;
} else {
System.out.println("Failed for " + key + ". Val incorrect.");
return "Password incorrect";
}
}
}
writer.write(line);
writer.newLine();
}
_file.delete();
temp.renameTo(_file);
return null;
} finally {
if (br != null) {
br.close();
}
if (writer != null) {
writer.close();
}
if (temp != null) {
temp.delete();
}
_fileLock.writeLock().unlock();
}
}
}
Any ideas guys? Thanks.
The issue is related to differences between the way Windows and Unix handle locks on files. On Unix, One process can be writing to a file, and another can open it to read it. Windows does not allow this.
Full disclosure: I expected Java to throw an IOException if it failed to perform IO-type stuff on files, briefly forgetting that many of those operations return boolean specifying whether or not the operation was successful.
Long story short, near the end of updateValueForKey(), where I delete _file, and rename tmp to _file, temp still has a FileWriter open against it, and _file still has a BufferedReader open against it. Basically, I had to move the _file.delete and temp.renameTo() below the finally block.
Related
I'm implementing a small tool in Java. I have a excel document and from every sheet I need to generate a .sql file. I've created an sql file model, which I have to read from for every excel sheet then replace a value and write it back to another .sql file. The problem is I use a for where I loop through my sheets and for every sheet I need to read that sql file, modify it and export it somewhere else. I get a "Stream closed" error, and I don't know how to close my buffer and/or my InputStream properly. Can you guys help me out with this ?
This is my code:
This gets everything from the file and converts it to a String
public String getString(InputStream is) throws IOException {
BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
String line;
try {
reader = new BufferedReader(new InputStreamReader(is));
while ((line = reader.readLine()) != null) {
sb.append(line + System.lineSeparator());
}
} catch (IOException ex) {
ex.printStackTrace();
}
return sb.toString();
}
This is used to export the file
public void exportFile(String text, String path, String name, String extension) {
BufferedWriter output = null;
try {
File sqlFile = new File(path + name + extension);
output = new BufferedWriter(new FileWriter(sqlFile));
output.write(text);
} catch (IOException e) {
e.printStackTrace();
logger.severe("Unable to write to file!\n");
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
logger.severe("Unable to close buffer\n");
}
}
}
}
And this a the part of my run() method, which uses the code above:
ClassLoader loader = this.getClass().getClassLoader();
InputStream createTableInputStream = loader.getResourceAsStream("val_table_create.sql");
if (createTableInputStream == null) {
logger.severe("No tempalte found for creating table!\n");
return;
}
List<Sheet> bookSheets = getSheets(book);
for (Sheet sheet : bookSheets) {
setHeader(table, sheet);
String exportText = getString(createTableInputStream);
exportText = exportText.replaceAll(TABLE_NAME, tableName);
// exportText = exportText.replaceAll(VAL_DATA_TYPE, valDataType);
// exportText = exportText.replaceAll(MSG_TEXT_DATA_TYPE, messageDataType);
exportFile(exportText, absoluteWorkspacePath + File.separator + outputPath + File.separator, tableName, ".sql");
}
if (createTableInputStream != null) {
createTableInputStream.close();
}
The Problem is in this method:
public String getString(InputStream is) throws IOException {
You close the the reader and stream at the end. (When you close the reader the streams in it are automatic close.
Edit: You should close the reader. getString(InputStream is) throws IOException returns always the same String or? Read it before you go in the loop and reuse the String everytime.
String exportText = getString("val_table_create.sql");
for (Sheet sheet : bookSheets) {
setHeader(table, sheet);
String newExportText = exportText.replaceAll(TABLE_NAME, tableName);
messageDataType);
exportFile(newExportText, absoluteWorkspacePath + File.separator + outputPath + File.separator, tableName, ".sql");
}
Change your getString Method to this:
public String getString(String resourceName) throws IOException {
BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
String line;
try {
InputStream createTableInputStream reader.getResourceAsStream(resourceName);
reader = new BufferedReader(new InputStreamReader(is));
while ((line = reader.readLine()) != null) {
sb.append(line + System.lineSeparator());
}
} catch (IOException ex) {
ex.printStackTrace();
}
return sb.toString();
}
and close there all the streams. Now you have one place where you load your file.
createTableInputStream will be closed for the first time you call getString method so for next sheet in loop you will get stream closed.
It's a better practice to close the stream in the method who created it. You should close the stream in run method instead.
I am trying to replace a string from a js file which have content like this
........
minimumSupportedVersion: '1.1.0',
........
now 'm trying to replace the 1.1.0 with 1.1.1. My code is searching the text but not replacing. Can anyone help me with this. Thanks in advance.
public class replacestring {
public static void main(String[] args)throws Exception
{
try{
FileReader fr = new FileReader("G:/backup/default0/default.js");
BufferedReader br = new BufferedReader(fr);
String line;
while((line=br.readLine()) != null) {
if(line.contains("1.1.0"))
{
System.out.println("searched");
line.replace("1.1.0","1.1.1");
System.out.println("String replaced");
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
}
First, make sure you are assigning the result of the replace to something, otherwise it's lost, remember, String is immutable, it can't be changed...
line = line.replace("1.1.0","1.1.1");
Second, you will need to write the changes back to some file. I'd recommend that you create a temporary file, to which you can write each `line and when finished, delete the original file and rename the temporary file back into its place
Something like...
File original = new File("G:/backup/default0/default.js");
File tmp = new File("G:/backup/default0/tmpdefault.js");
boolean replace = false;
try (FileReader fr = new FileReader(original);
BufferedReader br = new BufferedReader(fr);
FileWriter fw = new FileWriter(tmp);
BufferedWriter bw = new BufferedWriter(fw)) {
String line = null;
while ((line = br.readLine()) != null) {
if (line.contains("1.1.0")) {
System.out.println("searched");
line = line.replace("1.1.0", "1.1.1");
bw.write(line);
bw.newLine();
System.out.println("String replaced");
}
}
replace = true;
} catch (Exception e) {
e.printStackTrace();
}
// Doing this here because I want the files to be closed!
if (replace) {
if (original.delete()) {
if (tmp.renameTo(original)) {
System.out.println("File was updated successfully");
} else {
System.err.println("Failed to rename " + tmp + " to " + original);
}
} else {
System.err.println("Failed to delete " + original);
}
}
for example.
You may also like to take a look at The try-with-resources Statement and make sure you are managing your resources properly
If you're working with Java 7 or above, use the new File I/O API (aka NIO) as
// Get the file path
Path jsFile = Paths.get("C:\\Users\\UserName\\Desktop\\file.js");
// Read all the contents
byte[] content = Files.readAllBytes(jsFile);
// Create a buffer
StringBuilder buffer = new StringBuilder(
new String(content, StandardCharsets.UTF_8)
);
// Search for version code
int pos = buffer.indexOf("1.1.0");
if (pos != -1) {
// Replace if found
buffer.replace(pos, pos + 5, "1.1.1");
// Overwrite with new contents
Files.write(jsFile,
buffer.toString().getBytes(StandardCharsets.UTF_8),
StandardOpenOption.TRUNCATE_EXISTING);
}
I'm assuming your script file size doesn't cross into MBs; use buffered I/O classes otherwise.
How to list all folders in a directory without changing order even when new files are added, in a file using Java? When I run the program it is going to an infinite loop with "Not checked" comment which I put in order to look whether it is checking the file.
try
{
BufferedWriter bufferedWriter = null;
bufferedWriter = new BufferedWriter(new FileWriter("d://my.txt"));
BufferedReader br = new BufferedReader(new FileReader("d://my.txt"));
int i=1;
File f=new File("D:/Moviezzz");
File[] fi=f.listFiles();
for(File fil:fi)
{
if(!fil.isHidden() && (fil.isDirectory() || fil.isFile()))
{
int s=i++;
String files = fil.getName();
String thisLine;
while(null != (thisLine="\t"+br.readLine()))
{
String exist[]=thisLine.split("\t");
//ensure that it is already written in file
if(exist[0].equals(files))
{
System.out.println("Checked");
}
//ensure that it is not written in file
else
{
System.out.println("Not Checked");
}
}
bufferedWriter.write(s+"\t"+files);
bufferedWriter.newLine();
bufferedWriter.flush();
System.out.print("yes");
// bufferedWriter.write(s+" "+files);
}
br.close();
//Construct the BufferedWriter object
System.out.println("Succ");
}
}
catch(Exception ex)
{
System.out.println("failed");
}
You're getting an infinite loop, because the condition in your while loop can never be false.
while(null != (thisLine="\t"+br.readLine()))
you're essentially adding "\t" to the results of readLine(), so the value you're comparing to null will never actually be null. It will always have a \t in it.
After further research, it appears like thisLine will be be "\tnull" once you've reached the end of the stream
If I were you, I wouldn't do all that in your while loop. I'd do something more like this.
while(null != (thisLine=br.readLine()))
{
thisLine = "\t"+ + thisLine;
...
I have this piece of code
package Classes;
import java.io.*;
public class IpAdministrator {
public Boolean isActive(String ipAddress) {
boolean isActive = false;
String cmd;
String OS = System.getProperty("os.name");
System.out.println(OS);
String tmpfolder = System.getProperty("java.io.tmpdir");
System.out.println(tmpfolder);
//iptmp.deleteOnExit();
if (OS.equals("Linux")) {
cmd = "ping " + ipAddress + " -c 1";
} else {
cmd = "cmd /c ping " + ipAddress + " -n 1";
}
try {
String s = null;
Process p = Runtime.getRuntime().exec(cmd);
File iptmp = File.createTempFile("ipresult", ".txt", new File(tmpfolder));
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
s = s.toString();
BufferedWriter writer = new BufferedWriter(new FileWriter(iptmp));
writer.write(s);
}
} catch (Exception ex) {
System.out.println(ex.getMessage().toString());
}
return isActive;
}
}
I want to write the result from the command in the temporary file, I found something related in other questions in this site, and it seems to work fine, but when I run this, the file is created with some random numers (ie: ipresult540677216848957037.txt) and it's empty, I can't figure out why, I also read that it's something related to java 1.7, so that means that I can't fill the file with information, there something that I'm missing?
Every time you open a file for writing that way -- i.e., every time you execute this line:
BufferedWriter writer = new BufferedWriter(new FileWriter(iptmp));
the file is truncated to zero length. Furthermore, since you never explicitly call close() on the BufferedWriter, line you do write will never actually be flushed to the file. As a result, no data ever makes it to the disk.
To do this correctly, first, move the line above to before the loop, so it only executes once. Second, after the loop, include code like
if (writer != null)
writer.close();
Finally, note that your program is needlessly broken on Macs, which are neither Linux, nor do they use cmd.exe. Instead of the way you've written this, you test explicitly for Windows, and use the Windows command line if you find it; otherwise, assume something UNIX-like, and use the Linux version.
You need to close the writer
BufferedReader stdInput = new BufferedReader(new InputStreamReader(
p.getInputStream()));
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(iptmp));
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
s = s.toString();
writer.write(s);
}
} finally {
if (writer != null) {
writer.close();
}
}
If you are using java 7
try (BufferedWriter writer = new BufferedWriter(new FileWriter(
iptmp));) {
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
s = s.toString();
writer.write(s);
}
}
This code is reading a bunch of .java files and finding "public [classname]" or "private [classname]" and adding "System.out.println([classname])" to that line.
The problem is When I write that line back in I end up with a blank file
Can anyone see what I am doing wrong?
private static void work(ArrayList<File> fileList) {
for (int i = 0; i < fileList.size(); i++) {
replaceLines(fileList.get(i));
}
}
public static void replaceLines(File file) {
String path = file.getPath();
String fileNameLong = file.getName();
String fileName = null;
if (fileNameLong.contains(".java")) {
fileName = fileNameLong.substring(0, file.getName().indexOf("."));
}
if (fileName != null && fileName != "") {
System.out.println(fileName);
try {
//prepare reading
FileInputStream in = new FileInputStream(path);
BufferedReader br = new BufferedReader(
new InputStreamReader(in));
//prepare writing
FileWriter fw = new FileWriter(file);
PrintWriter out = new PrintWriter(fw);
String strLine;
while ((strLine = br.readLine()) != null) {
// Does it contain a public or private constructor?
boolean containsPrivateCon = strLine.contains("private "
+ fileName);
boolean containsPublicCon = strLine.contains("public "
+ fileName);
if (containsPrivateCon || containsPublicCon) {
int lastIndexOfBrack = strLine.lastIndexOf("{");
while (lastIndexOfBrack == -1) {
strLine = br.readLine();
lastIndexOfBrack = strLine.lastIndexOf("{");
}
if (lastIndexOfBrack != -1) {
String myAddition = "\n System.out.println(\""
+ fileName + ".java\"); \n";
String strLineModified = strLine.substring(0,
lastIndexOfBrack + 1)
+ myAddition
+ strLine.substring(lastIndexOfBrack + 1);
strLine = strLineModified;
}
}
out.write(strLine);
}
} catch (Exception e) {
System.out.println(e);
}
}
}
If you want to write to the same file you're reading from, you should either write to a copy of the file (different filename) and then rename the output file, or use RandomAccessFile interface to edit a file in-place.
Usually, the first solution will be much easier to implement than the second one; unless the files are huge (which is probably not the case with .java files), there is no real reason to use the second.
You forgot to flush and close the file. PrintWriter keeps a buffer and unless you explicitly flush() it, the data will (un)happily sit in the buffer and it will never be written to the output.
So you need to add this before the line catch (Exception e) {
out.flush();
out.close();
Note that this is only necessary for PrintWriter and PrintStream. All other output classes flush when you close them.