Deleting values from Project Preference store - eclipse

In my custom plugin I am creating some folders on a new Wizard and adding paths of those created folders in project preferences. Now while deleting the folder I need to remove that path from the store. For that I extended org.eclipse.ltk.core.refactoring.deleteParticipants extension. I also created a class extending Change class and inside it in the perform method I wrote following to remove the key-value from preferences.
public Change perform(IProgressMonitor pm) throws CoreException {
System.out.println("called");
IPath deletedFolderPath = deletedFolder.getProjectRelativePath().makeAbsolute();
IResource[] roots = {deletedFolder.getProject()};
String[] fileSearchPatterns = {"*.properties", "*.prefs"};
FileTextSearchScope searchScope = FileTextSearchScope.newSearchScope(roots, fileSearchPatterns, false);
String regex = deletedFolderPath.addTrailingSeparator().toString();
Pattern searchPattern = Pattern.compile(regex);
TextSearchRequestor requestor = new TextSearchRequestor() {
public boolean acceptPatternMatch(TextSearchMatchAccess matchAccess) throws CoreException {
IFile matchedFile = matchAccess.getFile();
if("prefs".equals(matchedFile.getFileExtension())){
String prefKey = "";
try {
prefKey = ProjectPreferences.getKeyForValue(regex, deletedFolder.getProject());
ProjectPreferences.removeKeyFromStore(prefKey, deletedFolder.getProject());
System.out.println("after");
}
catch (BackingStoreException e) {
throw new CoreException(null);
}
}
else if(".properties".equals(matchedFile.getFileExtension())) {
}
return true;
}
};
TextSearchEngine.create().search(searchScope, requestor, searchPattern, pm);
return null;
}
but it get stuck on the line after pressing "OK" on the delete wizard.
ProjectPreferences.removeKeyFromStore(prefKey, deletedFolder.getProject())
or more specifically on the flush statement written in the removeKeyFromStore method
public static void removeKeyFromStore(String key, IProject project) throws BackingStoreException {
IEclipsePreferences projPref = getPreferences(project);
projPref.remove(key);
projPref.flush();
}
What am I doing wrong here or any other better way to achieve the same functionality.
Thanks!!

Related

Eclipse JDT ListRewrite inserts new node at wrong places

I'm trying to add an annotation to some selected fields using Eclipse JDT infrastructure. However, this is not run as a plugin. I added all the required dependencies to a separate project so this can be run in batch mode. However I found out that, the ListRewrite is not inserting my annotation at the right place. I have given the code below. I initially get all the field declarations in a map using a visitor and then add them one by one using the code below.
FieldDeclaration fld = lVrblDet.listStringVarMap.get(propName);
final MarkerAnnotation autoWiredAnnotate = ast.newMarkerAnnotation(); autoWiredAnnotate.setTypeName(ast.newName("MyAnnot"));
lrw = rewriter.getListRewrite(fld, FieldDeclaration.MODIFIERS2_PROPERTY);
lrw.insertLast(autoWiredAnnotate, null);
Document document = new Document(cu.toString());
try {
TextEdit edits = rewriter.rewriteAST(document, null);
edits.apply(document);
} catch (MalformedTreeException | IllegalArgumentException | BadLocationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
However the expected output is sometimes offset by 1 character.The input and output classes have been given below.
Input Class:
#SuppressWarnings("unchecked")
public class SampleClassA {
public SampleClassB classB;
public SampleClassB getClassB() {
return classB;
}
public void setClassB(SampleClassB classB) {
this.classB = classB;
}
#Deprecated
public void printNameFromSmapleClassB() {
System.out.println(this.classB.name);
}
}
Output Class:
#SuppressWarnings("unchecked") public class SampleClassA {
p #MyAnnot
ublic SampleClassB classB;
public SampleClassB getClassB(){
return classB;
}
public void setClassB( SampleClassB classB){
this.classB=classB;
}
#Deprecated public void printNameFromSmapleClassB(){
System.out.println(this.classB.name);
}
}
As you can see in the code above, the Annotation messed with the modifier. I have tried multiple combinations of insertFirst,insertLast.Examples on the net are incomplete. Can somebody point me the mistake/the right resource ?
I just couldn't get it to work with ListRewrite. I don't know what I was doing wrong. So I wrote a visitor to store all the FieldDeclarations in a map.
#Override
public boolean visit(FieldDeclaration node) {
for (Object obj : node.fragments()) {
listStringVarMap.put(((VariableDeclarationFragment) obj).getName().toString(), node);
}
return false;
}
I looped through the map and inserted the annotation nodes as a modifiers, for the declarations that met my criteria. Please do remember to turn on recormodifications for the compilation unit you are modifying.
CompilationUnit cu = jFileAst.getEquivCompilUnit();
cu.recordModifications();
FieldDeclaration fldDecl = lVrblDet.listStringVarMap.get(propName);
importVo = (JavaAnnotImportVo) javaAstNodeCreator
.createASTNode(SpringAnnotationEnum.AutowireAnnotation, ast);
cu.imports().add(importVo.getImpDecl());
fldDecl.modifiers().add(0, importVo.getAnnotNode());
Finally write to file on disk/save back. Formatting(optional) before saving is a good idea, because the node insertions mess up with the code formatting.

WizardNewFileCreationPage specify folders

I use a WizardNewFileCreationPage to give a user the option to create a new file the project workspace.
However I want the user to create the file only in certain folders, for example in project/data not in project/data/trash folder.
How do I specify this criteria?
You can create a class which extends WizardNewFileCreationPage and override the validatePage method:
#Override
protected boolean validatePage()
{
boolean valid = super.validatePage();
if (!valid)
return false;
IPath containerPath = getContainerFullPath();
String fileName = getFileName();
valid = ... check containerPath and fileName meet your criteria
if (!valid)
{
setErrorMessage("your error message");
}
return valid;
}

Powershell monitoring for file system changes [duplicate]

I have an application where I am looking for a text file and if there are any changes made to the file I am using the OnChanged eventhandler to handle the event. I am using the NotifyFilters.LastWriteTime but still the event is getting fired twice. Here is the code.
public void Initialize()
{
FileSystemWatcher _fileWatcher = new FileSystemWatcher();
_fileWatcher.Path = "C:\\Folder";
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileWatcher.Filter = "Version.txt";
_fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
_fileWatcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
.......
}
In my case the OnChanged is called twice, when I change the text file version.txt and save it.
I am afraid that this is a well-known bug/feature of the FileSystemWatcher class. This is from the documentation of the class:
You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use a FileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.
Now this bit of text is about the Created event, but the same thing applies to other file events as well. In some applications you might be able to get around this by using the NotifyFilter property, but my experience is says that sometimes you have to do some manual duplicate filtering (hacks) as well.
A while ago I bookedmarked a page with a few FileSystemWatcher tips. You might want to check it out.
I've "fixed" that problem using the following strategy in my delegate:
// fsw_ is the FileSystemWatcher instance used by my application.
private void OnDirectoryChanged(...)
{
try
{
fsw_.EnableRaisingEvents = false;
/* do my stuff once asynchronously */
}
finally
{
fsw_.EnableRaisingEvents = true;
}
}
Any duplicated OnChanged events from the FileSystemWatcher can be detected and discarded by checking the File.GetLastWriteTime timestamp on the file in question. Like so:
DateTime lastRead = DateTime.MinValue;
void OnChanged(object source, FileSystemEventArgs a)
{
DateTime lastWriteTime = File.GetLastWriteTime(uri);
if (lastWriteTime != lastRead)
{
doStuff();
lastRead = lastWriteTime;
}
// else discard the (duplicated) OnChanged event
}
Here is my solution which helped me to stop the event being raised twice:
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;
Here I have set the NotifyFilter property with only Filename and size.
watcher is my object of FileSystemWatcher. Hope this will help.
Here's my approach :
// Consider having a List<String> named _changedFiles
private void OnChanged(object source, FileSystemEventArgs e)
{
lock (_changedFiles)
{
if (_changedFiles.Contains(e.FullPath))
{
return;
}
_changedFiles.Add(e.FullPath);
}
// do your stuff
System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
{
lock (_changedFiles)
{
_changedFiles.Remove(e.FullPath);
}
};
timer.Start();
}
This is the solution I used to solve this issue on a project where I was sending the file as attachment in a mail.
It will easily avoid the twice fired event even with a smaller timer interval but in my case 1000 was alright since I was happier with missing few changes than with flooding the mailbox with > 1 message per second.
At least it works just fine in case several files are changed at the exact same time.
Another solution I've thought of would be to replace the list with a dictionary mapping files to their respective MD5, so you wouldn't have to choose an arbitrary interval since you wouldn't have to delete the entry but update its value, and cancel your stuff if it hasn't changed.
It has the downside of having a Dictionary growing in memory as files are monitored and eating more and more memory, but I've read somewhere that the amount of files monitored depends on the FSW's internal buffer, so maybe not that critical.
Dunno how MD5 computing time would affect your code's performances either, careful =\
My scenario is that I have a virtual machine with a Linux server in it. I am developing files on the Windows host. When I change something in a folder on the host I want all the changes to be uploaded, synced onto the virtual server via Ftp. This is how I do eliminate the duplicate change event when I write to a file ( which flags the folder containing the file to be modified as well ) :
private Hashtable fileWriteTime = new Hashtable();
private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
string path = e.FullPath.ToString();
string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();
// if there is no path info stored yet
// or stored path has different time of write then the one now is inspected
if ( !fileWriteTime.ContainsKey(path) ||
fileWriteTime[path].ToString() != currentLastWriteTime
)
{
//then we do the main thing
log( "A CHANGE has occured with " + path );
//lastly we update the last write time in the hashtable
fileWriteTime[path] = currentLastWriteTime;
}
}
Mainly I create a hashtable to store file write time information. Then if the hashtable has the filepath that is modified and it's time value is the same as the currently notified file's change then I know it is the duplicate of the event and ignore it.
I have created a Git repo with a class that extends FileSystemWatcher to trigger the events only when copy is done. It discards all the changed events exept the last and it raise it only when the file become available for read.
Download FileSystemSafeWatcher and add it to your project.
Then use it as a normal FileSystemWatcher and monitor when the events are triggered.
var fsw = new FileSystemSafeWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;
Try with this code:
class WatchPlotDirectory
{
bool let = false;
FileSystemWatcher watcher;
string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";
public WatchPlotDirectory()
{
watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Filter = "*.*";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
watcher.EnableRaisingEvents = true;
}
void OnChanged(object sender, FileSystemEventArgs e)
{
if (let==false) {
string mgs = string.Format("File {0} | {1}",
e.FullPath, e.ChangeType);
Console.WriteLine("onchange: " + mgs);
let = true;
}
else
{
let = false;
}
}
void OnRenamed(object sender, RenamedEventArgs e)
{
string log = string.Format("{0} | Renamed from {1}",
e.FullPath, e.OldName);
Console.WriteLine("onrenamed: " + log);
}
public void setPath(string path)
{
this.path = path;
}
}
I know this is an old issue, but had the same problem and none of the above solution really did the trick for the problem I was facing. I have created a dictionary which maps the file name with the LastWriteTime. So if the file is not in the dictionary will go ahead with the process other wise check to see when was the last modified time and if is different from what it is in the dictionary run the code.
Dictionary<string, DateTime> dateTimeDictionary = new Dictionary<string, DateTime>();
private void OnChanged(object source, FileSystemEventArgs e)
{
if (!dateTimeDictionary.ContainsKey(e.FullPath) || (dateTimeDictionary.ContainsKey(e.FullPath) && System.IO.File.GetLastWriteTime(e.FullPath) != dateTimeDictionary[e.FullPath]))
{
dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);
//your code here
}
}
One possible 'hack' would be to throttle the events using Reactive Extensions for example:
var watcher = new FileSystemWatcher("./");
Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
.Throttle(new TimeSpan(500000))
.Subscribe(HandleChangeEvent);
watcher.EnableRaisingEvents = true;
In this case I'm throttling to 50ms, on my system that was enough, but higher values should be safer. (And like I said, it's still a 'hack').
I spent some significant amount of time using the FileSystemWatcher, and some of the approaches here will not work. I really liked the disabling events approach, but unfortunately, it doesn't work if there is >1 file being dropped, second file will be missed most if not all times.
So I use the following approach:
private void EventCallback(object sender, FileSystemEventArgs e)
{
var fileName = e.FullPath;
if (!File.Exists(fileName))
{
// We've dealt with the file, this is just supressing further events.
return;
}
// File exists, so move it to a working directory.
File.Move(fileName, [working directory]);
// Kick-off whatever processing is required.
}
I have a very quick and simple workaround here, it does work for me, and no matter the event would be triggered once or twice or more times occasionally, check it out:
private int fireCount = 0;
private void inputFileWatcher_Changed(object sender, FileSystemEventArgs e)
{
fireCount++;
if (fireCount == 1)
{
MessageBox.Show("Fired only once!!");
dowork();
}
else
{
fireCount = 0;
}
}
}
Here is a new solution you can try. Works well for me. In the event handler for the changed event programmatically remove the handler from the designer output a message if desired then programmatically add the handler back. example:
public void fileSystemWatcher1_Changed( object sender, System.IO.FileSystemEventArgs e )
{
fileSystemWatcher1.Changed -= new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
MessageBox.Show( "File has been uploaded to destination", "Success!" );
fileSystemWatcher1.Changed += new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
}
The main reason was
first event's last access time was current time(file write or changed time).
then second event was file's original last access time.
I solve under code.
var lastRead = DateTime.MinValue;
Watcher = new FileSystemWatcher(...)
{
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
Filter = "*.dll",
IncludeSubdirectories = false,
};
Watcher.Changed += (senderObject, ea) =>
{
var now = DateTime.Now;
var lastWriteTime = File.GetLastWriteTime(ea.FullPath);
if (now == lastWriteTime)
{
return;
}
if (lastWriteTime != lastRead)
{
// do something...
lastRead = lastWriteTime;
}
};
Watcher.EnableRaisingEvents = true;
This code worked for me.
private void OnChanged(object source, FileSystemEventArgs e)
{
string fullFilePath = e.FullPath.ToString();
string fullURL = buildTheUrlFromStudyXML(fullFilePath);
System.Diagnostics.Process.Start("iexplore", fullURL);
Timer timer = new Timer();
((FileSystemWatcher)source).Changed -= new FileSystemEventHandler(OnChanged);
timer.Interval = 1000;
timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
timer.Start();
}
private void t_Elapsed(object sender, ElapsedEventArgs e)
{
((Timer)sender).Stop();
theWatcher.Changed += new FileSystemEventHandler(OnChanged);
}
mostly for future me :)
I wrote a wrapper using Rx:
public class WatcherWrapper : IDisposable
{
private readonly FileSystemWatcher _fileWatcher;
private readonly Subject<FileSystemEventArgs> _infoSubject;
private Subject<FileSystemEventArgs> _eventSubject;
public WatcherWrapper(string path, string nameFilter = "*.*", NotifyFilters? notifyFilters = null)
{
_fileWatcher = new FileSystemWatcher(path, nameFilter);
if (notifyFilters != null)
{
_fileWatcher.NotifyFilter = notifyFilters.Value;
}
_infoSubject = new Subject<FileSystemEventArgs>();
_eventSubject = new Subject<FileSystemEventArgs>();
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Changed").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Created").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Deleted").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Renamed").Select(e => e.EventArgs)
.Subscribe(_infoSubject.OnNext);
// this takes care of double events and still works with changing the name of the same file after a while
_infoSubject.Buffer(TimeSpan.FromMilliseconds(20))
.Select(x => x.GroupBy(z => z.FullPath).Select(z => z.LastOrDefault()).Subscribe(
infos =>
{
if (infos != null)
foreach (var info in infos)
{
{
_eventSubject.OnNext(info);
}
}
});
_fileWatcher.EnableRaisingEvents = true;
}
public IObservable<FileSystemEventArgs> FileEvents => _eventSubject;
public void Dispose()
{
_fileWatcher?.Dispose();
_eventSubject.Dispose();
_infoSubject.Dispose();
}
}
Usage:
var watcher = new WatcherWrapper(_path, "*.info");
// all more complicated and scenario specific filtering of events can be done here
watcher.FileEvents.Where(x => x.ChangeType != WatcherChangeTypes.Deleted).Subscribe(x => //do stuff)
Try this, It's working fine
private static readonly FileSystemWatcher Watcher = new FileSystemWatcher();
static void Main(string[] args)
{
Console.WriteLine("Watching....");
Watcher.Path = #"D:\Temp\Watcher";
Watcher.Changed += OnChanged;
Watcher.EnableRaisingEvents = true;
Console.ReadKey();
}
static void OnChanged(object sender, FileSystemEventArgs e)
{
try
{
Watcher.Changed -= OnChanged;
Watcher.EnableRaisingEvents = false;
Console.WriteLine($"File Changed. Name: {e.Name}");
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
finally
{
Watcher.Changed += OnChanged;
Watcher.EnableRaisingEvents = true;
}
}
I wanted to react only on the last event, just in case, also on a linux file change it seemed that the file was empty on the first call and then filled again on the next and did not mind loosing some time just in case the OS decided to do some file/attribute change.
I am using .NET async here to help me do the threading.
private static int _fileSystemWatcherCounts;
private async void OnChanged(object sender, FileSystemEventArgs e)
{
// Filter several calls in short period of time
Interlocked.Increment(ref _fileSystemWatcherCounts);
await Task.Delay(100);
if (Interlocked.Decrement(ref _fileSystemWatcherCounts) == 0)
DoYourWork();
}
I think the best solution to solve the issue is to use reactive extensions
When you transform event into observable, then you can just add Throttling(..) (originally called Debounce(..))
Sample code here
var templatesWatcher = new FileSystemWatcher(settingsSnapshot.Value.TemplatesDirectory)
{
NotifyFilter = NotifyFilters.LastWrite,
IncludeSubdirectories = true
};
templatesWatcher.EnableRaisingEvents = true;
Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
addHandler => templatesWatcher.Changed += addHandler,
removeHandler => templatesWatcher.Changed -= removeHandler)
.Throttle(TimeSpan.FromSeconds(5))
.Subscribe(args =>
{
_logger.LogInformation($"Template file {args.EventArgs.Name} has changed");
//TODO do something
});
You could try to open it for write, and if successful then you could assume the other application is done with the file.
private void OnChanged(object source, FileSystemEventArgs e)
{
try
{
using (var fs = File.OpenWrite(e.FullPath))
{
}
//do your stuff
}
catch (Exception)
{
//no write access, other app not done
}
}
Just opening it for write appears not to raise the changed event. So it should be safe.
FileReadTime = DateTime.Now;
private void File_Changed(object sender, FileSystemEventArgs e)
{
var lastWriteTime = File.GetLastWriteTime(e.FullPath);
if (lastWriteTime.Subtract(FileReadTime).Ticks > 0)
{
// code
FileReadTime = DateTime.Now;
}
}
Sorry for the grave dig, but I've been battling this issue for a while now and finally came up with a way to handle these multiple fired events. I would like to thank everyone in this thread as I have used it in many references when battling this issue.
Here is my complete code. It uses a dictionary to track the date and time of the last write of the file. It compares that value, and if it is the same, it suppresses the events. It then sets the value after starting the new thread.
using System.Threading; // used for backgroundworker
using System.Diagnostics; // used for file information
private static IDictionary<string, string> fileModifiedTable = new Dictionary<string, string>(); // used to keep track of our changed events
private void fswFileWatch_Changed( object sender, FileSystemEventArgs e )
{
try
{
//check if we already have this value in our dictionary.
if ( fileModifiedTable.TryGetValue( e.FullPath, out sEmpty ) )
{
//compare timestamps
if ( fileModifiedTable[ e.FullPath ] != File.GetLastWriteTime( e.FullPath ).ToString() )
{
//lock the table
lock ( fileModifiedTable )
{
//make sure our file is still valid
if ( File.Exists( e.FullPath ) )
{
// create a new background worker to do our task while the main thread stays awake. Also give it do work and work completed handlers
BackgroundWorker newThreadWork = new BackgroundWorker();
newThreadWork.DoWork += new DoWorkEventHandler( bgwNewThread_DoWork );
newThreadWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bgwNewThread_RunWorkerCompleted );
// capture the path
string eventFilePath = e.FullPath;
List<object> arguments = new List<object>();
// add arguments to pass to the background worker
arguments.Add( eventFilePath );
arguments.Add( newEvent.File_Modified );
// start the new thread with the arguments
newThreadWork.RunWorkerAsync( arguments );
fileModifiedTable[ e.FullPath ] = File.GetLastWriteTime( e.FullPath ).ToString(); //update the modified table with the new timestamp of the file.
FILE_MODIFIED_FLAG.WaitOne(); // wait for the modified thread to complete before firing the next thread in the event multiple threads are being worked on.
}
}
}
}
}
catch ( IOException IOExcept )
{
//catch any errors
postError( IOExcept, "fswFileWatch_Changed" );
}
}
Event if not asked, it is a shame there are no ready solution samples for F#.
To fix this here is my recipe, just because I can and F# is a wonderful .NET language.
Duplicated events are filtered out using FSharp.Control.Reactive package, which is just a F# wrapper for reactive extensions. All that can be targeted to full framework or netstandard2.0:
let createWatcher path filter () =
new FileSystemWatcher(
Path = path,
Filter = filter,
EnableRaisingEvents = true,
SynchronizingObject = null // not needed for console applications
)
let createSources (fsWatcher: FileSystemWatcher) =
// use here needed events only.
// convert `Error` and `Renamed` events to be merded
[| fsWatcher.Changed :> IObservable<_>
fsWatcher.Deleted :> IObservable<_>
fsWatcher.Created :> IObservable<_>
//fsWatcher.Renamed |> Observable.map renamedToNeeded
//fsWatcher.Error |> Observable.map errorToNeeded
|] |> Observable.mergeArray
let handle (e: FileSystemEventArgs) =
printfn "handle %A event '%s' '%s' " e.ChangeType e.Name e.FullPath
let watch path filter throttleTime =
// disposes watcher if observer subscription is disposed
Observable.using (createWatcher path filter) createSources
// filter out multiple equal events
|> Observable.distinctUntilChanged
// filter out multiple Changed
|> Observable.throttle throttleTime
|> Observable.subscribe handle
[<EntryPoint>]
let main _args =
let path = #"C:\Temp\WatchDir"
let filter = "*.zip"
let throttleTime = TimeSpan.FromSeconds 10.
use _subscription = watch path filter throttleTime
System.Console.ReadKey() |> ignore
0 // return an integer exit code
In my case need to get the last line of a text file that is inserted by other application, as soon as insertion is done. Here is my solution. When the first event is raised, i disable the watcher from raising others, then i call the timer TimeElapsedEvent because when my handle function OnChanged is called i need the size of the text file, but the size at that time is not the actual size, it is the size of the file imediatelly before the insertion. So i wait for a while to proceed with the right file size.
private FileSystemWatcher watcher = new FileSystemWatcher();
...
watcher.Path = "E:\\data";
watcher.NotifyFilter = NotifyFilters.LastWrite ;
watcher.Filter = "data.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
...
private void OnChanged(object source, FileSystemEventArgs e)
{
System.Timers.Timer t = new System.Timers.Timer();
try
{
watcher.Changed -= new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = false;
t.Interval = 500;
t.Elapsed += (sender, args) => t_Elapsed(sender, e);
t.Start();
}
catch(Exception ex) {
;
}
}
private void t_Elapsed(object sender, FileSystemEventArgs e)
{
((System.Timers.Timer)sender).Stop();
//.. Do you stuff HERE ..
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
Here is another approach. Instead of propagating the first event of a quick succession of events and suppressing all that follow, now all are suppressed except from the last one. I think that the scenarios that can benefit from this approach are more common.
To make this happen we must use a sliding delay. Every incoming event cancels the timer that would fire the previous event, and restarts the timer. This opens the possibility that a never-ending series of events will delay the propagation forever. To keep things simple, there is no provision for this abnormal case in the extension methods below.
public static class FileSystemWatcherExtensions
{
public static IDisposable OnAnyEvent(this FileSystemWatcher source,
WatcherChangeTypes changeTypes, FileSystemEventHandler handler, int delay)
{
var cancellations = new Dictionary<string, CancellationTokenSource>(
StringComparer.OrdinalIgnoreCase);
var locker = new object();
if (changeTypes.HasFlag(WatcherChangeTypes.Created))
source.Created += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Deleted))
source.Deleted += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Changed))
source.Changed += FileSystemWatcher_Event;
if (changeTypes.HasFlag(WatcherChangeTypes.Renamed))
source.Renamed += FileSystemWatcher_Event;
return new Disposable(() =>
{
source.Created -= FileSystemWatcher_Event;
source.Deleted -= FileSystemWatcher_Event;
source.Changed -= FileSystemWatcher_Event;
source.Renamed -= FileSystemWatcher_Event;
});
async void FileSystemWatcher_Event(object sender, FileSystemEventArgs e)
{
var key = e.FullPath;
var cts = new CancellationTokenSource();
lock (locker)
{
if (cancellations.TryGetValue(key, out var existing))
{
existing.Cancel();
}
cancellations[key] = cts;
}
try
{
await Task.Delay(delay, cts.Token);
// Omitting ConfigureAwait(false) is intentional here.
// Continuing in the captured context is desirable.
}
catch (TaskCanceledException)
{
return;
}
lock (locker)
{
if (cancellations.TryGetValue(key, out var existing)
&& existing == cts)
{
cancellations.Remove(key);
}
}
cts.Dispose();
handler(sender, e);
}
}
public static IDisposable OnAllEvents(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.All, handler, delay);
public static IDisposable OnCreated(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Created, handler, delay);
public static IDisposable OnDeleted(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Deleted, handler, delay);
public static IDisposable OnChanged(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Changed, handler, delay);
public static IDisposable OnRenamed(this FileSystemWatcher source,
FileSystemEventHandler handler, int delay)
=> OnAnyEvent(source, WatcherChangeTypes.Renamed, handler, delay);
private struct Disposable : IDisposable
{
private readonly Action _action;
internal Disposable(Action action) => _action = action;
public void Dispose() => _action?.Invoke();
}
}
Usage example:
myWatcher.OnAnyEvent(WatcherChangeTypes.Created | WatcherChangeTypes.Changed,
MyFileSystemWatcher_Event, 100);
This line combines the subscription to two events, the Created and the Changed. So it is roughly equivalent to these:
myWatcher.Created += MyFileSystemWatcher_Event;
myWatcher.Changed += MyFileSystemWatcher_Event;
The difference is that the two events are regarded as a single type of event, and in case of a quick succession of these events only the last one will be propagated. For example if a Created event is followed by two Changed events, and there is no time gap larger than 100 msec between these three events, only the second Changed event will be propagated by invoking the MyFileSystemWatcher_Event handler, and the previous ones will be discarded.
I have changed the way I monitor files in directories. Instead of using the FileSystemWatcher I poll locations on another thread and then look at the LastWriteTime of the file.
DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);
Using this information and keeping an index of a file path and it's latest write time I can determine files that have changed or that have been created in a particular location. This removes me from the oddities of the FileSystemWatcher. The main downside is that you need a data structure to store the LastWriteTime and the reference to the file, but it is reliable and easy to implement.
I was able to do this by added a function that checks for duplicates in an buffer array.
Then perform the action after the array has not been modified for X time using a timer:
- Reset timer every time something is written to the buffer
- Perform action on tick
This also catches another duplication type. If you modify a file inside a folder, the folder also throws a Change event.
Function is_duplicate(str1 As String) As Boolean
If lb_actions_list.Items.Count = 0 Then
Return False
Else
Dim compStr As String = lb_actions_list.Items(lb_actions_list.Items.Count - 1).ToString
compStr = compStr.Substring(compStr.IndexOf("-") + 1).Trim
If compStr <> str1 AndAlso compStr.parentDir <> str1 & "\" Then
Return False
Else
Return True
End If
End If
End Function
Public Module extentions
<Extension()>
Public Function parentDir(ByVal aString As String) As String
Return aString.Substring(0, CInt(InStrRev(aString, "\", aString.Length - 1)))
End Function
End Module
This solution worked for me on production application:
Environment:
VB.Net Framework 4.5.2
Set manually object properties: NotifyFilter = Size
Then use this code:
Public Class main
Dim CalledOnce = False
Private Sub FileSystemWatcher1_Changed(sender As Object, e As IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
If (CalledOnce = False) Then
CalledOnce = True
If (e.ChangeType = 4) Then
' Do task...
CalledOnce = False
End If
End Sub
End Sub
Try this!
string temp="";
public void Initialize()
{
FileSystemWatcher _fileWatcher = new FileSystemWatcher();
_fileWatcher.Path = "C:\\Folder";
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileWatcher.Filter = "Version.txt";
_fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
_fileWatcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
.......
if(temp=="")
{
//do thing you want.
temp = e.name //name of text file.
}else if(temp !="" && temp != e.name)
{
//do thing you want.
temp = e.name //name of text file.
}else
{
//second fire ignored.
}
}
I had to combine several ideas from the posts above and add file locking check to get it working for me:
FileSystemWatcher fileSystemWatcher;
private void DirectoryWatcher_Start()
{
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher
{
Path = #"c:\mypath",
NotifyFilter = NotifyFilters.LastWrite,
Filter = "*.*",
EnableRaisingEvents = true
};
fileSystemWatcher.Changed += new FileSystemEventHandler(DirectoryWatcher_OnChanged);
}
private static void WaitUntilFileIsUnlocked(String fullPath, Action<String> callback, FileAccess fileAccess = FileAccess.Read, Int32 timeoutMS = 10000)
{
Int32 waitMS = 250;
Int32 currentMS = 0;
FileInfo file = new FileInfo(fullPath);
FileStream stream = null;
do
{
try
{
stream = file.Open(FileMode.Open, fileAccess, FileShare.None);
stream.Close();
callback(fullPath);
return;
}
catch (IOException)
{
}
finally
{
if (stream != null)
stream.Dispose();
}
Thread.Sleep(waitMS);
currentMS += waitMS;
} while (currentMS < timeoutMS);
}
private static Dictionary<String, DateTime> DirectoryWatcher_fileLastWriteTimeCache = new Dictionary<String, DateTime>();
private void DirectoryWatcher_OnChanged(Object source, FileSystemEventArgs ev)
{
try
{
lock (DirectoryWatcher_fileLastWriteTimeCache)
{
DateTime lastWriteTime = File.GetLastWriteTime(ev.FullPath);
if (DirectoryWatcher_fileLastWriteTimeCache.ContainsKey(ev.FullPath))
{
if (DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath].AddMilliseconds(500) >= lastWriteTime)
return; // file was already handled
}
DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath] = lastWriteTime;
}
Task.Run(() => WaitUntilFileIsUnlocked(ev.FullPath, fullPath =>
{
// do the job with fullPath...
}));
}
catch (Exception e)
{
// handle exception
}
}

Eclipse Plugin, How can I know when IResourceDeltaVisitor ends processing tree nodes?

I wrote an Eclipse Plugin that basically allow a programmer to select a Java source from the Project Explorer and by selecting the corresponding DropDown menu option it will creates an interface .java file based on the one selected.
Everything works fine, but now I need to program the update part of the job.
The update requierement is simple, I need to listen for changes and identify that the sources that have the interface generated have been modified and recreate the interface file.
To do this I wrote a class that implements IResourceChangeListener interface.
That class looks like:
public class DTOChangeListener implements IResourceChangeListener {
private List<UpdatedUnit> updatedUnits;
public DTOChangeListener() {
super();
this.updatedUnits=new ArrayList<UpdatedUnit>();
}
#Override
public void resourceChanged(IResourceChangeEvent event) {
try{
if(event.getType() == IResourceChangeEvent.POST_CHANGE){
event.getDelta().accept(this.buildVisitor());
}
}catch(CoreException ex){
ex.printStackTrace();
}
}
protected IResourceDeltaVisitor buildVisitor(){
IResourceDeltaVisitor result=new IResourceDeltaVisitor() {
#Override
public boolean visit(IResourceDelta resDelta) throws CoreException {
String resName=resDelta.getResource().getName();
if(resName==null || resName.equals("")){
return true;
}
String[] splits=resName.split("\\.");
String name = splits[0];
if(name.contains("PropertyAccess")){
return false;
}
String interfaceName=name + "PropertyAccess";
String interfaceFile=interfaceName + ".java";
IResource res=resDelta.getResource();
if((res instanceof IFolder) || (res instanceof IProject)){
// Avoid Folder & Project Nodes
return true;
}
IProject project=res.getProject();
if(project!=null){
if(project.isNatureEnabled("org.eclipse.jdt.core.javanature")){
IJavaElement element=JavaCore.create(res);
if(element instanceof ICompilationUnit){
ICompilationUnit unit=(ICompilationUnit)element;
IPath path=res.getProjectRelativePath().removeLastSegments(1);
IResource propertyAccess=project.findMember(path.append(interfaceFile));
if(propertyAccess!=null){
UpdatedUnit updatedUnit=new UpdatedUnit(project, path, unit);
updatedUnits.add(updatedUnit);
return false;
}
}
}
}
return true;
}
};
return result;
}
public List<UpdatedUnit> getUpdatedUnits() {
return updatedUnits;
}
}
I add the Listener to the Workspace, now the question I have is:
How can I know when the updatedUnits List is completed in order to proccess the list with my own code?
One posible answer to this question would be, don't worry, the:
event.getData().accept(this.buildVisitor());
will block until proccessing of the visitor finish.
but at least is not documented like it would.
Any ideas would be appreciated.
Thanks in Advance.
Daniel
Unless it's documented to not block, it blocks.

Custom IronPython import resolution

I am loading an IronPython script from a database and executing it. This works fine for simple scripts, but imports are a problem. How can I intercept these import calls and then load the appropriate scripts from the database?
EDIT: My main application is written in C# and I'd like to intercept the calls on the C# side without editing the Python scripts.
EDIT: From the research I've done, it looks like creating your own PlatformAdaptationLayer is the way you're supposed to to implement this, but it doesn't work in this case. I've created my own PAL and in my testing, my FileExsists method gets called for every import in the script. But for some reason it never calls any overload of the OpenInputFileStream method. Digging through the IronPython source, once FileExists returns true, it tries to locate the file itself on the path. So this looks like a dead end.
After a great deal of trial and error, I arrived at a solution. I never managed to get the PlatformAdaptationLayer approach to work correctly. It never called back to the PAL when attempting to load the modules.
So what I decided to do was replace the built-in import function by using the SetVariable method as shown below (Engine and Scope are protected members exposing the ScriptEngine and ScriptScope for the parent script):
delegate object ImportDelegate(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple tuple);
protected void OverrideImport()
{
ScriptScope scope = IronPython.Hosting.Python.GetBuiltinModule(Engine);
scope.SetVariable("__import__", new ImportDelegate(DoDatabaseImport));
}
protected object DoDatabaseImport(CodeContext context, string moduleName, PythonDictionary globals, PythonDictionary locals, PythonTuple tuple)
{
if (ScriptExistsInDb(moduleName))
{
string rawScript = GetScriptFromDb(moduleName);
ScriptSource source = Engine.CreateScriptSourceFromString(rawScript);
ScriptScope scope = Engine.CreateScope();
Engine.Execute(rawScript, scope);
Microsoft.Scripting.Runtime.Scope ret = Microsoft.Scripting.Hosting.Providers.HostingHelpers.GetScope(scope);
Scope.SetVariable(moduleName, ret);
return ret;
}
else
{ // fall back on the built-in method
return IronPython.Modules.Builtin.__import__(context, moduleName);
}
}
Hope this helps someone!
I was just trying to do the same thing, except I wanted to store my scripts as embedded resources. I'm creating a library that is a mixture of C# and IronPython and wanted to distribute it as a single dll. I wrote a PlatformAdaptationLayer that works, it first looks in the resources for the script that's being loaded, but then falls back to the base implementation which looks in the filesystem. Three parts to this:
Part 1, The custom PlatformAdaptationLayer
namespace ZenCoding.Hosting
{
internal class ResourceAwarePlatformAdaptationLayer : PlatformAdaptationLayer
{
private readonly Dictionary<string, string> _resourceFiles = new Dictionary<string, string>();
private static readonly char Seperator = Path.DirectorySeparatorChar;
private const string ResourceScriptsPrefix = "ZenCoding.python.";
public ResourceAwarePlatformAdaptationLayer()
{
CreateResourceFileSystemEntries();
}
#region Private methods
private void CreateResourceFileSystemEntries()
{
foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
if (!name.EndsWith(".py"))
{
continue;
}
string filename = name.Substring(ResourceScriptsPrefix.Length);
filename = filename.Substring(0, filename.Length - 3); //Remove .py
filename = filename.Replace('.', Seperator);
_resourceFiles.Add(filename + ".py", name);
}
}
private Stream OpenResourceInputStream(string path)
{
string resourceName;
if (_resourceFiles.TryGetValue(RemoveCurrentDir(path), out resourceName))
{
return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
}
return null;
}
private bool ResourceDirectoryExists(string path)
{
return _resourceFiles.Keys.Any(f => f.StartsWith(RemoveCurrentDir(path) + Seperator));
}
private bool ResourceFileExists(string path)
{
return _resourceFiles.ContainsKey(RemoveCurrentDir(path));
}
private static string RemoveCurrentDir(string path)
{
return path.Replace(Directory.GetCurrentDirectory() + Seperator, "").Replace("." + Seperator, "");
}
#endregion
#region Overrides from PlatformAdaptationLayer
public override bool FileExists(string path)
{
return ResourceFileExists(path) || base.FileExists(path);
}
public override string[] GetFileSystemEntries(string path, string searchPattern, bool includeFiles, bool includeDirectories)
{
string fullPath = Path.Combine(path, searchPattern);
if (ResourceFileExists(fullPath) || ResourceDirectoryExists(fullPath))
{
return new[] { fullPath };
}
if (!ResourceDirectoryExists(path))
{
return base.GetFileSystemEntries(path, searchPattern, includeFiles, includeDirectories);
}
return new string[0];
}
public override bool DirectoryExists(string path)
{
return ResourceDirectoryExists(path) || base.DirectoryExists(path);
}
public override Stream OpenInputFileStream(string path)
{
return OpenResourceInputStream(path) ?? base.OpenInputFileStream(path);
}
public override Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share)
{
return OpenResourceInputStream(path) ?? base.OpenInputFileStream(path, mode, access, share);
}
public override Stream OpenInputFileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
{
return OpenResourceInputStream(path) ?? base.OpenInputFileStream(path, mode, access, share, bufferSize);
}
#endregion
}
}
You would need to change the constant ResourceScriptsPrefix to whatever your base namespace is where you stored the python scripts.
Part 2, The custom ScriptHost
namespace ZenCoding.Hosting
{
internal class ResourceAwareScriptHost : ScriptHost
{
private readonly PlatformAdaptationLayer _layer = new ResourceAwarePlatformAdaptationLayer();
public override PlatformAdaptationLayer PlatformAdaptationLayer
{
get { return _layer; }
}
}
}
Part 3, finally, how to get a Python engine using your custom stuff:
namespace ZenCoding.Hosting
{
internal static class ResourceAwareScriptEngineSetup
{
public static ScriptEngine CreateResourceAwareEngine()
{
var setup = Python.CreateRuntimeSetup(null);
setup.HostType = typeof(ResourceAwareScriptHost);
var runtime = new ScriptRuntime(setup);
return runtime.GetEngineByTypeName(typeof(PythonContext).AssemblyQualifiedName);
}
}
}
It would be easy to change this to load scripts from some other location, like a database. Just change the OpenResourceStream, ResourceFileExists and ResourceDirectoryExists methods.
Hope this helps.
You can re-direct all I/O to the database using the PlatformAdaptationLayer. To do this you'll need to implement a ScriptHost which provides the PAL. Then when you create the ScriptRuntime you set the HostType to your host type and it'll be used for the runtime. On the PAL you then override OpenInputFileStream and return a stream object which has the content from the database (you could just use a MemoryStream here after reading from the DB).
If you want to still provide access to file I/O you can always fall back to FileStream's for "files" you can't find.
You need to implement import hooks. Here's an SO question with pointers: PEP 302 Example: New Import Hooks