For the purpose of making a backup, I copy a directory with tests scripts to another location. For this I'm using Files.walkFileTree(fromDir, SimpleFileVisitor).
This works as expected and I can copy it multiple times without any problems.
But when I delete the target directory via Windows File explorer I get a java.nio.file.FileAlreadyExistsException on the target directory when I try to make a back up.
I'm pretty sure that the target directory is not there.
When I close the Java program and start it again, it works again. It is as if manually deleting the directory locks the 'non-existing' target directory and Java throws a FileAlreadyExistsException.
Here are the relevant parts of the SimpleFileVisitor
I'm using these options when copying the files
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
Path copyDir = toDir.resolve(fromDir.relativize(dir));
try {
Files.createDirectories(copyDir);
}
catch(IOException ioe) {
LOG.error("Failed to create directory " + copyDir.toString());
throw ioe;
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
Path toFile = toDir.resolve(fromDir.relativize(file));
try {
Files.copy(file, toFile, opts);
}
catch(IOException ioe) {
LOG.error("Failed to create file " + toFile.toString());
throw ioe;
}
return FileVisitResult.CONTINUE;
}
I was expecting that deleting the target directory via Windows File Explorer would not cause this problem, but apparently it does.
Is there anything I could do to make the copy action possible in case someone deletes the directory via Windows File Explorer.
Related
I am getting a classic well-known error in my Springboot app when it tries to load a static resource using getResourceAsStream():
java.io.FileNotFoundException: class path resource [blah blah blah] cannot be opened because it does not exist
While doing some basic troubleshooting, I opened the contents of the generated jar file and found that everything in the jar is contained inside a folder named BOOT-INF.
Why is everything inside BOOT-INF?
How do I get rid of this?
UPDATE
Here's the actual relevant part of the code
ClassPathResource cpr = new ClassPathResource(RESOURCE_CLASSPATH_PREFIX + url);
try (InputStream is = cpr.getInputStream()) {
return DigestUtils.sha1Hex(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
Additional Clue
The failing code is inside a ServletFilter. I think this has something to do with class loaders.
I am working with spring boot and spring content. I want to store all my pictures and videos in one directory but my code continues to create different dir every time I rerun the application
I have such bean and when I run the app again it shows null pointer because the dir already exists but I want it to create it just once and every file is stored there
every time i run this tries to create the dir again
#Bean
File filesystemRoot() {
try {
return Files.createDirectory(Paths.get("/tmp/photo_video_myram")).toFile();
} catch (IOException io) {}
return null;
}
#Bean
FileSystemResourceLoader fileSystemResourceLoader() {
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
One solution, would be to check if the directory exists:
#Bean
File filesystemRoot() {
File tmpDir = new File("tmp/photo_video_myram");
if (!tmpDir.isDirectory()) {
try {
return Files.createDirectory(tmpDir.toPath()).toFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return tmpDir;
}
You can use isDirectory() method first to check if the directory already exists. In case it does not exist, then create a new one.
Meanwhile there is another way to achieve this, when you use Spring Boot and accordingly spring-content-fs-boot-starter.
According to the documentation at https://paulcwarren.github.io/spring-content/refs/release/fs-index.html#_spring_boot_configuration it should be sufficient to add
spring.content.fs.filesystemRoot=/tmp/photo_video_myram
to your application.properties file.
while working on an ftp server with the vfs2 library I noticed, that I had to enable VFS.setUriStyle(true) so the library would change the working directory to parent directory of the target file I am operating on (cwd directoryName).
But if UriStyle is enabled, everything is being resolved relativly to the root. Which would not be a Problem if the root was not "//".
The class GenericFileName sets the absolutePath of the root to "/", which makes the Method getPath() return "/"+getUriTrailer() which in the case of the root always returns "//". Everything that is resolved relativly to // has two dots proceeding to their path.
Which means if I execute the following code:
public class RemoteFileTest {
public static void main(String[] args) {
// Options for a RemoteFileObject connection
VFS.setUriStyle(true);
FileSystemOptions options = new FileSystemOptions();
// we doing an ftp connection, hence we use the ftpConfigBuilder
// we want to work in passive mode
FtpFileSystemConfigBuilder.getInstance().setPassiveMode(options, true);
FtpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(options, false);
// DefaultFileSystemConfigBuilder.getInstance().setRootURI(options, "/newRoot/");
// System.out.println(DefaultFileSystemConfigBuilder.getInstance().getRootURI(options));
// ftp://localhost:21/
StaticUserAuthenticator auth = new StaticUserAuthenticator("", "user", "pass");
try {
DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(options, auth);
} catch (FileSystemException e) {
e.printStackTrace();
return;
}
// A FileSystemManager creates an abstract FileObject linked to are desired RemoteFile.
// That link is just simulated and not yet real.
FileSystemManager manager;
try {
manager = VFS.getManager();
} catch (FileSystemException e) {
e.printStackTrace();
return;
}
try (FileObject remoteFile = manager.resolveFile("ftp://localhost:21/sub_folder/test.txt", options)) {
System.out.println("Is Folder " + remoteFile.isFolder());
System.out.println("Is File " + remoteFile.isFile());
} catch (FileSystemException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
}}
I receive this interaction with the ftp server:
USER user
PASS ****
TYPE I
CWD //
SYST
PASV
LIST ..sub_folder/
PWD
CWD ..sub_folder/
I want the interaction to be just like this, but without the two dots infront of the directory.
Kind regards
Barry
Fixed it as described below:
Disabled uriStyle again.
Wrote my own VFS class which creates my custom written Manager.
That Manager overwrites the FtpFileProvider with my custom one, which simply sets the root to a custom selected one, which causes the desired behaviour.
import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystem;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.impl.DefaultFileSystemConfigBuilder;
import org.apache.commons.vfs2.provider.ftp.FtpFileProvider;
public class AdvancedFtpFileProvider extends FtpFileProvider {
public AdvancedFtpFileProvider() {
super();
// setFileNameParser(AdvancedFtpFileNameParser.getInstance());
}
#Override
protected FileObject findFile(FileName name, FileSystemOptions fileSystemOptions) throws FileSystemException {
// Check in the cache for the file system
//getContext().getFileSystemManager().resolveName... resolves the configured RootUri relative to the selected root (name.getRoot()). This calls cwd to the selectedRoot and operates from there with relatives urls towards the new root!
final FileName rootName = getContext().getFileSystemManager().resolveName(name.getRoot(), DefaultFileSystemConfigBuilder.getInstance().getRootURI(fileSystemOptions));
final FileSystem fs = getFileSystem(rootName, fileSystemOptions);
// Locate the file
// return fs.resolveFile(name.getPath());
return fs.resolveFile(name);
}
}
Came across this question because I was having the same issue with the following
ftp://user:pass#host//home/user/file.txt
becoming... (note the single slash after 'home')
ftp://user:pass#host/home/user/file.txt
I did this to solve the issue...
// Setup some options, add as many as you need
FileSystemOptions opts = new FileSystemOptions( );
// This line tells VFS to treat the URI as the absolute path and not relative
FtpsFileSystemConfigBuilder.getInstance( ).setUserDirIsRoot( opts, false );
// Retrieve the file from the remote FTP server
FileObject realFileObject = fileSystemManager.resolveFile( fileSystemUri, opts );
I hope this can help someone, if not then provide a reference for the next time this stumps me.
I am using visual studio installer.
but when uninstalling - from add remove program it does not delete all files but leaves the folder with some files, how can I cause the uninstall delete all files?
The installer only removes the files it installed. The files created the post installation are not removed. You must create a custom action to perform the cleanup.
To clean up after your software (delete some custom or user generated files), you should create a Custom Action and add it to the Uninstall section of your installer.
A custom action can be a Class Library that inherits from the System.Configuration.Install.Installer class.
Here's a sample implementation of the Uninstaller Custom Action:
[RunInstaller(true)]
public partial class CustomUninstaller : System.Configuration.Install.Installer
{
public CustomUninstaller()
{
InitializeComponent();
}
public override void Uninstall(IDictionary savedState)
{
if (savedState != null)
{
base.Uninstall(savedState);
}
string targetDir = #"C:\Your\Installation\Path";
string tempDir = Path.Combine(targetDir, "temp");
try
{
// delete temp files (you can as well delete all files: "*.*")
foreach (FileInfo f in new DirectoryInfo(targetDir).GetFiles("*.tmp"))
{
f.Delete();
}
// delete entire temp folder
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
}
catch (Exception ex)
{
// TODO: Handle exceptions here
}
}
}
I have searched numerous articles and tried several different ways of solving this issue.
Note that this process works fine in a Console app or a Windows app with the exact same code. So, there's nothing wrong with the logic. It's the way the Windows Service is processing these files that is somehow not letting subsequent files get through.
I tried reading a simple article to actually debug the service, but I don't have the "Attach To Process" selection under my Debug menu using Visual Studio 2010.
http://msdn.microsoft.com/en-us/library/7a50syb3(v=vs.100).aspx
I really need some help here, because my options have really run out.
Here is the simple scenario:
I have a FileSystemWatcher in the OnStart method that kicks off another method to process a file on my local machine. These are just simple txt files. When the first file is processed, everything is normal and I get the following in my Event log:
Data successfully inserted for file 20121212_AZM_Journey_MIS.txt
When I try and process subsequent files (exact same format, but a different name --- with the date), I get the following in my Event log:
The process cannot access the file 'C:\Projects\Data\VendingStats\20121213_AZM_Journey_MIS.txt' because it is being used by another process.
Note that the files are watched for in this same directory when created.
Here is my code in my Windows Service. Can someone please let me know how I can solve this issue? Note that e.FullPath evaluates to "C:\Projects\Data\VendingStats" on my local machine.
public partial class VendingStatsService : ServiceBase
{
public VendingStatsService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
FileSystemWatcher Watcher = new FileSystemWatcher(#"C:\Projects\Data\VendingStats");
Watcher.EnableRaisingEvents = true;
Watcher.Created += new FileSystemEventHandler(**Watcher_Created**);
Watcher.Filter = "*.txt";
Watcher.IncludeSubdirectories = false;
}
private void Watcher_Created(object sender, FileSystemEventArgs e)
{
try
{
using (TextReader tr = new StreamReader(e.FullPath))
{
// code here to insert data from file into a DB
}
Dispose();
}
catch (Exception ex)
{
EventLog.WriteEntry("VendorStats", "Error in the Main:" + "\r\n\r\n" + ex.Message + "\r\n\r\n" + ex.InnerException);
return;
}
}
public void Dispose()
{
GC.Collect();
}