Note: I am aware there are several questions similar to this one, however, I cannot find any which explain how to resolve the situation I am trying to resolve. I will ask this question with a specific example, for which I need a solution.
Consider the code:
private final void writeToFile(final File parent, final String filename, final Charset charset, final String content) throws IOException {
final File file = new File(parent, filename);
if (file.exists()) {
LOG.warn("File {} already exists, file will be replaced.", file.getCanonicalPath());
if (!file.delete()) {
logAndThrow(String.format("Cannot delete file '%s'.", file.getCanonicalPath()), null);
}
}
try (final FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(fos, charset)) {
writer.write(content);
}
}
I am trying to write a unit test to provoke the IOException being thrown when the code cannnot delete the file. The unit test I have tried is as follows:
#Test public void testFileNotDeletable() throws IOException {
final File file = new File(folder.getRoot(), formattedFile.getMetaData().getFormattedCaptureFileName());
file.createNewFile();
try {
file.setReadOnly();
exception.expect(IOException.class);
exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));
writer.write(formattedFile);
} finally {
file.setWritable(true);
}
}
I have also tried locking the file:
#Test public void testFileNotDeletable() throws IOException {
final File file = new File(folder.getRoot(), formattedFile.getMetaData().getFormattedCaptureFileName());
file.createNewFile();
try (FileInputStream fis = new FileInputStream(file)) {
final FileLock lock = fis.getChannel().tryLock(0L, Long.MAX_VALUE, true);
try {
exception.expect(IOException.class);
exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));
writer.write(formattedFile);
} finally {
lock.release();
}
}
}
No matter what I try, the file.delete() successfully deletes the file, and the test fails, as the expected IOException was not thrown.
Any help greatly appreciated.
Note: Added for clarification, some extra code that shows that the File object is completely separate in the environments. The formattedFile being passed to the write method is not a File or sub-class of File, it is one of our internal classes. The File in the JUnit test is using a TemporaryFolder for the root, the formattedFile has a MetaData item, which determines the filename. In my JUnit test I am trying to create an empty file, which cannot be deleted, in the location that my actual code will attempt to write the file. I need file.delete() to return false, so that I can test the exception is being thrown. I therefore cannot mock a File object.
There are two solutions to your question, I recommend the first one.
Solution 1
You are not testing the java file I/O operations/class here, you are testing your code's functional behaviour in response to file operation. So, ideally in your JUnit you should be mocking the File object & its respective calls, and only focus on testing your code.
Solution 2
If you still wish to test full integration with java file IO, open file in write mode before attempting to delete, and it will take care of your test case.
NOTE: Code tested in CENTOS, WINDOWS, UBUNTU, MAC OS-X
Subject Class:
public class FileSolution {
public void fileHandler(File file) throws IOException, Exception {
if (file.exists()) {
LOG.warn("File {} already exists, file will be replaced.",
file.getCanonicalPath());
if (!file.delete()) {
logAndThrow(String.format("Cannot delete file '%s'.",
file.getCanonicalPath()),
new IOException(String.format("Cannot delete file '%s'.",
file.getCanonicalPath())));
}
}
}
}
Subject Uner Test:
import static org.mockito.BDDMockito.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class FileSolutionTest {
#Rule
public final ExpectedException exception = ExpectedException.none();
/**
* Solution 1
*
* #throws Exception
*/
#Test
public void testFileNotDeletableWithMock() throws Exception {
final File file = mock(File.class);
file.createNewFile();
// mock file & IO operations
given(file.exists()).willReturn(true);
given(file.delete()).willReturn(false);
given(file.getCanonicalPath()).willReturn("test.txt");
exception.expect(IOException.class);
exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));
new FileSolution().fileHandler(file);
}
/**
* Solution 2
*
* #throws Exception
*/
#Test
public void testFileNotDeletable() throws Exception {
File file = null;
FileWriter fileWriter = null;
try{
file = new File("test.txt");
file.createNewFile();
file.deleteOnExit();
exception.expect(IOException.class);
exception.expectMessage(String.format("Cannot delete file '%s'.", file.getCanonicalPath()));
// open file with another process for writing
fileWriter = new FileWriter(file, true);
new FileSolution().fileHandler(file);
} finally{
if(fileWriter != null){
fileWriter.flush();
fileWriter.close();
}
}
}
}
I totally agree with Turing85 about using mockito.
Let's imagine you have an original class with a method similar to the one you want to test:
public class FileDel {
public void logOnIOException(File file) throws IOException {
if (file.exists()) {
LOG.warn("File {} already exists, file will be replaced.", file.getCanonicalPath());
if (!file.delete()) {
logAndThrow(String.format("Cannot delete file '%s'.", file.getCanonicalPath()), null);
}
}
}
public void logAndThrow(String msg, String s) {
//Do nothing
}
private static class LOG {
public static void warn(String msg, String path) {
}
}
}
Then you can trigger an inner exception in this way:
#RunWith(MockitoJUnitRunner.class)
public class FileDelTest {
#Test(expected = IOException.class)
public void testFileNotDeletable() throws IOException {
File file = mock(File.class);
when(file.exists()).thenReturn(true);
when(file.delete()).thenAnswer(new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock iom) throws Throwable {
throw new IOException();
}
});
FileDel f = new FileDel();
try {
f.methodToTest(file);
} finally {
}
}
}
What about open InputStream for this file and do not close it. Until file's descriptor will not be closed, file will not be deleted.
In order to prevent a file from being deleted you have to deny the security permission in windows. From the UI we would need to do something like
Right-click the file or document in your PC => Choose Properties;
In Security, tab Edit to change permission => Select Add and enter Everyone;
Press OK and select the group to change Full control permission to Deny;
Press Yes to confirm.
The only way I know of to change file permissions with Java are:
file.setExecutable(true|false);
file.setReadable(true|false);
file.setWritable(true|false);
and
File file = new File("test.txt");
if(file.exists())
{
//Setting file permissions for owner, group and others using PosixFilePermission
HashSet<PosixFilePermission> set = new HashSet<PosixFilePermission>();
//Adding owner's file permissions
set.add(PosixFilePermission.OWNER_EXECUTE);
set.add(PosixFilePermission.OWNER_READ);
set.add(PosixFilePermission.OWNER_WRITE);
//Adding group's file permissions
set.add(PosixFilePermission.GROUP_EXECUTE);
set.add(PosixFilePermission.GROUP_READ);
set.add(PosixFilePermission.GROUP_WRITE);
//Adding other's file permissions
set.add(PosixFilePermission.OTHERS_EXECUTE);
set.add(PosixFilePermission.OTHERS_READ);
set.add(PosixFilePermission.OTHERS_WRITE);
Files.setPosixFilePermissions(Paths.get("test.txt"), set);
}
else
{
System.out.println("Sorry...File doesn't exist.");
}
So, preventing a file from being deleted I would assume would have to do with file writing permissions. Try disabling the writable and maybe the executable permissions before trying to delete the file.
If this doesn't work then I do not believe it can be done with the Java language yet as these are the only methods available at the moment for changing file permissions. I could be wrong, but I have been unable to find anything else.
UPDATE
For Linux try the following:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellComand {
public static void main(String[] args) {
ExecuteShellComand obj = new ExecuteShellComand();
String command = "sudo chattr +i /backups/passwd";
// OR try
//String command = "sudo chattr +i -V /backups/passwd";
String output = obj.executeCommand(command);
System.out.println(output);
}
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
The above makes /backups/passwd file immutable (or undeletable). This implies that the file can’t be modified in any way: it can’t be deleted or renamed. You can’t even create a link to it and no data can be written to the file as well.
That's about the only thing I can think of.
Hope this helps.
In Linux you can, with the chattr command set a file that is "immutable" that cannot be deleted even by root. Someone else said "set file permissions" which is right, but did not give specific detail.
Cheers
D
Related
I am trying to read an .arff file in Weka. I did this code. I keep getting error not sure about my work.
public String fileArff(String filePath) throws Exception
{
try {
BufferedReader br = new BufferedReader(new FileReader(filePath));
ArffReader re = new ArffReader(br);
Instances data = re.getData();
data.setClassIndex(data.numAttributes()-1);
File file = new File(filePath);
if (file.exists() && file.isFile() && file.canRead()) {
return "The file exists";
}
while (data != null)
{
re.appened(data);
re.appened("\n");
data = br.getData();
}
return re.toString();
}
catch (IOException e)
{
return "There is an error";
}
}
I am trying to read a .arff file in java language, and I used Weka library.
The code after the line data.setClassIndex is either unnecessary or not valid (like re.appened or br.getData). I recommend reading the Javadoc documentation of the Weka API for the relevant classes that you want to use.
The following code has the method readArff, which reads an ARFF file using the DataSource class (this class can the Weka Loader to use automatically based on the file's extension) and returns the Instances dataset object that it generated from it. It will throw an IOException if the file does not exist.
The main method calls the readArff method, expecting one argument (the path to the ARFF file to read) to be supplied when executing the ArffHelper class.
import weka.core.Instances;
import weka.core.converters.ConverterUtils;
import java.io.File;
import java.io.IOException;
public class ArffHelper {
public Instances readArff(String path) throws Exception {
if (!new File(path).exists())
throw new IOException("File does not exist: " + path);
Instances data = ConverterUtils.DataSource.read(path);
data.setClassIndex(data.numAttributes() - 1); // assuming that the class is the last attribute
return data;
}
public static void main(String[] args) throws Exception {
ArffHelper b = new ArffHelper();
Instances data = b.readArff(args[0]);
System.out.println(data);
}
}
package ideat;
import java.nio.file.Paths;
import java.io.FileWriter;
import java.util.Scanner;
public class Paaohjelma {
public static void main(String[] args) {
try {
Scanner tLuk = new Scanner(Paths.get("ideat.txt"));
FileWriter tKirj = new FileWriter("ideat.txt");
for (String line = tLuk.nextLine(); line.isBlank(); tKirj.append("\n")) {
tKirj.write("textHere");
}
tKirj.close();
tLuk.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
I created a loop that goes trough the txt-file until it finds an empty line to write, however this doesn't work because when Scanner tries to read next line that is empty, java throws no next line exception. The purpose of the program is to add and save new ideas to a text file and that is why I don't want to overwrite existing lines of text.
Once you open a file you are reading in write mode, a file descriptor for reading is corrupted. It could be not corrupted, but once written something, it will be corrupted especially when what the descriptor will read overwritten.
So, you should take rewrite and replace approach:
try {
Scanner tLuk = new Scanner(Paths.get("ideat.txt"));
FileWriter tKirj = new FileWriter("ideat.txt.tmp");
while (tLuk.hasNextLine()) {
String line = tLuk.nextLine();
if (line.isBlank()) {
tKirj.write("textHere");
} else {
tKirj.write(line);
}
}
tKirj.close();
tLuk.close();
} catch (Exception e) {
Then, replace the file ideat.txt with ideat.txt.tmp.
Files.move(
Paths.get("ideat.txt.tmp"),
Paths.get("ideat.txt"),
StandardCopyOption.REPLACE_EXISTING
);
I am trying to write code for a word guessing game, and it works well when I use bufferedreader and inputstream combined. But when I try it using scanner, it cannot find the file, even though in both instances the file is in the same folder. It is in a folder called res under the src folder in my project folder(I am coding in eclipse).
import java.util.ArrayList;
import java.util.Scanner;
import java.io.File;
public class WordGen {
private final String filename = "/res/words.txt";
File file = new File(filename);
Scanner input = null;
private ArrayList<String> list = new ArrayList<>();
public WordGen() {
try {
input = new Scanner(file);
while (input.hasNextLine()) {
String w = input.nextLine();
list.add(w);
}
} catch (Exception ex) {
System.out.println("File not found.");
}
}
public String getword() {
if (list.isEmpty()) {
return "NOTHING";
}
return list.get((int) (Math.random() * list.size()));
}
}
public class test {
public static void main(String[] args) {
WordGen wordgen = new WordGen();
System.out.println(wordgen.getword());
}
}
I tried searching for this problem but couldn't find it here. I am guessing it's a very small error which I cannot figure out. Thanks and regards.
EDIT: Here's the other code that worked(Everything else same as before):
public WordGenerator()
{
try(InputStream input = getClass().getResourceAsStream(fileName);
BufferedReader bfreader = new BufferedReader(new InputStreamReader(input)))
{
String line = "";
while ((line = bfreader.readLine()) != null)
words.add(line);
}
catch (Exception e)
{
System.out.println("Couldn't find file");
}
}
Scanner is trying to load a file - and you're providing an absolute filename, /res/words.txt.
In order to create an InputStream, you're loading a resource, giving it an absolute resource name, even though you've called the variable fileName:
getClass().getResourceAsStream(fileName)
That works because it can load a resource called /res/words.txt from the classpath, but it's not loading a file with a filename of /res/words.txt.
You could use a filename of res/words.txt, if you run the code from the src directory... or you could just stick to using getResourceAsStream, which is probably a better idea as it doesn't rely on your working directory, and will continue to work even if your code and resources are packaged up into a jar file.
If you really want to use Scanner, you could always use new Scanner(input) - there's a Scanner constructor accepting an InputStream.
My problem is that whenever I run my program from Eclipse or IntelliJ I can create as many files in a specific folder but when I create a jar it only creates one file no matter how many times I call the method that does this.
In detail a feature of a program allows the user to create a file in the folder where the jar is executed from. This is done through a class containing static fields and methods.
The fields in the class are:
private static StringBuilder sb = new StringBuilder();
private static Formatter formatter = new Formatter(sb);
private static BufferedWriter bw;
private static String filesFolderPath;
First I get the path to the jar:
private static String getJarPath() throws UnsupportedEncodingException
{
URL url = PrintHelper.class.getProtectionDomain().getCodeSource().getLocation();
String jarPath = URLDecoder.decode(url.getFile(), "UTF-8");
String path = new File(jarPath).getParentFile().getPath();
return path;
}
Then I set the path to the folder I want the files to be put in and create that folder through a static block in the class so that this code is executed only once when the class is first used:
static
{
try
{
String dirPath = getJarPath();
String fileSeparator = System.getProperty("file.separator");
filesFolderPath = dirPath + fileSeparator + "files" + fileSeparator;
File file = new File(filesFolderPath);
file.mkdir();
} catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
Finally. each time a user wants to create a file this method is called:
public static void print(String filename) throws IOException, FileNotFoundException
{
File file = new File(filesFolderPath, getFileName(filename));
bw = new BufferedWriter(new FileWriter(file));
//writing with the buffered writer
bw.close();
}
Is there any reason for this to work as I want it when run from an IDE and create only one file when running from the jar?
I am trying to extract an archive .tar.gz using java and I am getting Directory error that I do not seem to understand. Please help. I got this sample code from https://forums.oracle.com/forums/thread.jspa?threadID=2065236
package untargz;
import java.io.*;
import com.ice.tar.*;
import javax.activation.*;
import java.util.zip.GZIPInputStream;
/**
*
* #author stanleymungai
*/
public class Untargz {
public static InputStream getInputStream(String tarFileName) throws Exception{
if(tarFileName.substring(tarFileName.lastIndexOf(".") + 1, tarFileName.lastIndexOf(".") + 3).equalsIgnoreCase("gz")){
System.out.println("Creating an GZIPInputStream for the file");
return new GZIPInputStream(new FileInputStream(new File(tarFileName)));
}else{
System.out.println("Creating an InputStream for the file");
return new FileInputStream(new File(tarFileName));
}
}
private static void untar(InputStream in, String untarDir) throws IOException {
System.out.println("Reading TarInputStream... ");
TarInputStream tin = new TarInputStream(in);
TarEntry tarEntry = tin.getNextEntry();
if(new File(untarDir).exists()){
while (tarEntry != null){
File destPath = new File(untarDir + File.separatorChar + tarEntry.getName());
System.out.println("Processing " + destPath.getAbsoluteFile());
if(!tarEntry.isDirectory()){
FileOutputStream fout = new FileOutputStream(destPath);
tin.copyEntryContents(fout);
fout.close();
}else{
destPath.mkdir();
}
tarEntry = tin.getNextEntry();
}
tin.close();
}else{
System.out.println("That destination directory doesn't exist! " + untarDir);
}
}
private void run(){
try {
String strSourceFile = "C:/AskulInstaller/pid.tar.gz";
String strDest = "C:/AskulInstaller/Extracted Files";
InputStream in = getInputStream(strSourceFile);
untar(in, strDest);
}catch(Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
}
public static void main(String[] args) {
new Untargz().run();
}
}
Once I run this piece of code, this is My Output;
Creating an GZIPInputStream for the file
Reading TarInputStream...
That destination directory doesn't exist! C:/AskulInstaller/Extracted Files
BUILD SUCCESSFUL (total time: 0 seconds)
When I Manually Create the destination Directory C:/AskulInstaller/Extracted Files
I get this Error Output;
Creating an GZIPInputStream for the file
Reading TarInputStream...
Processing C:\AskulInstaller\Extracted Files\AskulInstaller\pid\Askul Logs\DbLayer_AskulMain_10_Apr_2013_07_44.log
java.io.FileNotFoundException: C:\AskulInstaller\Extracted Files\AskulInstaller\pid\Askul Logs\DbLayer_AskulMain_10_Apr_2013_07_44.log (The system cannot find the path specified)
C:\AskulInstaller\Extracted Files\AskulInstaller\pid\Askul Logs\DbLayer_AskulMain_10_Apr_2013_07_44.log (The system cannot find the path specified)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:212)
at java.io.FileOutputStream.<init>(FileOutputStream.java:165)
at untargz.Untargz.untar(Untargz.java:37)
at untargz.Untargz.run(Untargz.java:55)
at untargz.Untargz.main(Untargz.java:64)
Is there a way I am supposed to place My directories so that the extraction Happens or what exactly is My Mistake?
If the tar file contains an entry for a file foo/bar.txt but doesn't contain a previous directory entry for foo/ then your code will be trying to create a file in a directory that doesn't exist. Try adding
destFile.getParentFile().mkdirs();
just before you create the FileOutputStream.
Alternatively, if you don't mind your code depending on Ant as a library then you can delegate the whole unpacking process to an Ant task rather than doing it by hand. Something like this (not fully tested):
Project p = new Project();
Untar ut = new Untar();
ut.setProject(p);
ut.setSrc(tarFile);
if(tarFile.getName().endsWith(".gz")) {
ut.setCompression((UntarCompressionMethod)EnumeratedAttribute.getInstance(UntarCompressionMethod.class, "gzip"));
}
ut.setDest(destDir);
ut.perform();