I am using Resource Change Listener to track the changes done to my project. This listener is invoked if I delete, or create or save any changes to a file, in the project. I have the ResourceDelta object. With this, how can I find if the file is being created or is going to be deleted.
Below is my code:
In the activator class of my plugin I have:
IResourceChangeListener listener = new MyResourceChangeListener();
this.workspace.addResourceChangeListener(this.listener);
In the MyResourceChangeListener class I have:
System.out.println(event.getBuildKind());
System.out.println(event.getSource());
System.out.println(event.getType());
if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
System.out.println("this is post change event");
final IResourceDelta delta = event.getDelta();
System.out.println(delta.getFlags());
System.out.println(delta.getKind());
System.out.println(delta.getFlags());
if (delta.getKind() == IResourceDelta.ADDED) {
System.out.println("this is ADD event");
}
if (delta.getKind() == IResourceDelta.CHANGED) {
System.out.println("this is CHANGED event");
}
if ((delta.getFlags() & IResourceDelta.CONTENT) == 0) {
System.out.println("this is CONTENT event");
}
}
Output is always as below, either i create a class, delete a class, or make changes and save a class :
0
org.eclipse.core.internal.resources.Workspace#5f9f1f42
1
this is post change event
0
4
0
this is CHANGED event
this is CONTENT event
How can I differentiate between save, delete or create events.
Test the bitmap returned by IResourceChangeEvent.getType() for PRE_DELETE bit. Register the listener specifically for that event type with IWorkspace.addResourceChangeListener(IResourceChangeListener, int)
This article might be useful, too.
Related
The Code A and Image A is from the artical LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case).
The author told me "Trigger the event by setting a new Event as a new value", I think it should be "Trigger the event by setting a new Event as any value", right?
For example,
Step 1: The user clicks the button in master Activity with the code userClicksOnButton("StartDetails") , the Details Activity will start.
Step 2: The user presses back, coming back to the master activity
Step 3: The user clicks the button in master Activity with the code userClicksOnButton("StartDetails") again, the Details Activity will start again.
Is it right?
Code A
class ListViewModel : ViewModel {
private val _navigateToDetails = MutableLiveData<Event<String>>()
val navigateToDetails : LiveData<Event<String>>
get() = _navigateToDetails
fun userClicksOnButton(itemId: String) {
_navigateToDetails.value = Event(itemId) // Trigger the event by setting a new Event as a new value
}
}
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
myViewModel.navigateToDetails.observe(this, Observer {
it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
startActivity(DetailsActivity...)
}
})
Image A
You're having two different constraints on the data.
The LiveData emits every update to active observers, but the event itself is stateful. Thus a new event is required as a new LiveData value. The itemId could be any value, but the comment doesn't refer to it.
Trigger the event by setting a new Event as [a new] value
I have a process where I have 3 sequential user tasks (something like Task 1 -> Task 2 -> Task 3). So, to validate the Task 3, I have to validate the Task 1, then the Task 2.
My goal is to implement a workaround to go back in an execution of a process instance thanks to a Command like suggested in this link. The problem is I started to implement the command by it does not work as I want. The algorithm should be something like:
Retrieve the task with the passed id
Get the process instance of this task
Get the historic tasks of the process instance
From the list of the historic tasks, deduce the previous one
Create a new task from the previous historic task
Make the execution to point to this new task
Maybe clean the task pointed before the update
So, the code of my command is like that:
public class MoveTokenCmd implements Command<Void> {
protected String fromTaskId = "20918";
public MoveTokenCmd() {
}
public Void execute(CommandContext commandContext) {
HistoricTaskInstanceEntity currentUserTaskEntity = commandContext.getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstanceById(fromTaskId);
ExecutionEntity currentExecution = commandContext.getExecutionEntityManager()
.findExecutionById(currentUserTaskEntity.getExecutionId());
// Get process Instance
HistoricProcessInstanceEntity historicProcessInstanceEntity = commandContext
.getHistoricProcessInstanceEntityManager()
.findHistoricProcessInstance(currentUserTaskEntity.getProcessInstanceId());
HistoricTaskInstanceQueryImpl historicTaskInstanceQuery = new HistoricTaskInstanceQueryImpl();
historicTaskInstanceQuery.processInstanceId(historicProcessInstanceEntity.getId()).orderByExecutionId().desc();
List<HistoricTaskInstance> historicTaskInstances = commandContext.getHistoricTaskInstanceEntityManager()
.findHistoricTaskInstancesByQueryCriteria(historicTaskInstanceQuery);
int index = 0;
for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
if (historicTaskInstance.getId().equals(currentUserTaskEntity.getId())) {
break;
}
index++;
}
if (index > 0) {
HistoricTaskInstance previousTask = historicTaskInstances.get(index - 1);
TaskEntity newTaskEntity = createTaskFromHistoricTask(previousTask, commandContext);
currentExecution.addTask(newTaskEntity);
commandContext.getTaskEntityManager().insert(newTaskEntity);
AtomicOperation.TRANSITION_CREATE_SCOPE.execute(currentExecution);
} else {
// TODO: find the last task of the previous process instance
}
// To overcome the "Task cannot be deleted because is part of a running
// process"
TaskEntity currentUserTask = commandContext.getTaskEntityManager().findTaskById(fromTaskId);
if (currentUserTask != null) {
currentUserTask.setExecutionId(null);
commandContext.getTaskEntityManager().deleteTask(currentUserTask, "jumped to another task", true);
}
return null;
}
private TaskEntity createTaskFromHistoricTask(HistoricTaskInstance historicTaskInstance,
CommandContext commandContext) {
TaskEntity newTaskEntity = new TaskEntity();
newTaskEntity.setProcessDefinitionId(historicTaskInstance.getProcessDefinitionId());
newTaskEntity.setName(historicTaskInstance.getName());
newTaskEntity.setTaskDefinitionKey(historicTaskInstance.getTaskDefinitionKey());
newTaskEntity.setProcessInstanceId(historicTaskInstance.getExecutionId());
newTaskEntity.setExecutionId(historicTaskInstance.getExecutionId());
return newTaskEntity;
}
}
But the problem is I can see my task is created, but the execution does not point to it but to the current one.
I had the idea to use the activity (via the object ActivityImpl) to set it to the execution but I don't know how to retrieve the activity of my new task.
Can someone help me, please?
Unless somethign has changed in the engine significantly the code in the link you reference should still work (I have used it on a number of projects).
That said, when scanning your code I don't see the most important command.
Once you have the current execution, you can move the token by setting the current activity.
Like I said, the code in the referenced article used to work and still should.
Greg
Referring the same link in your question, i would personally recommend to work with the design of you your process. use an exclusive gateway to decide whether the process should end or should be returned to the previous task. if the generation of task is dynamic, you can point to the same task and delete local variable. Activiti has constructs to save your time from implementing the same :).
Parts of our UI uses IObservableElementEnumerable.EnumerableChanged in order to update if the user e.g. deletes a domain object from a folder.
When the UI is disposed, we unsubscribe from the event... or so we thought. It turns out that the unsubscribe doesn't have any effect, and our event handler is still called. This caused a number of odd bugs, but also leads to memory leaks.
The only time unsubscription works, is if we store the IObservableElementEnumerable reference instead of calling IObservableElementEnumerableFactory.GetEnumerable(obj) again. But this, in turn, is likely to keep a live reference to the folder object, which will break if the folder itself is deleted by the user.
This is particularly puzzling as the GetEnumerable() documentation clearly states: "It is expected that subsequent calls with the same domain object will yield the same instance of IObservableElementEnumerable." Is this not to be interpreted as a guarantee?
Should there be any reason for unsubscription not working?
The following code replicates the issue on Petrel 2011 (add to a simple plugin with a menu extension, or get the full solution here (DropBox)):
using System;
using System.Linq;
using System.Windows.Forms;
using Slb.Ocean.Core;
using Slb.Ocean.Petrel;
using Slb.Ocean.Petrel.Basics;
using Slb.Ocean.Petrel.UI;
namespace ObservableElementEnumerable
{
public class OEEForm : Form
{
private Droid _droid;
private bool _disposed;
public OEEForm()
{
IInput input = PetrelProject.Inputs;
IIdentifiable selected = input.GetSelected<object>().FirstOrDefault() as IIdentifiable;
if (selected == null)
{
PetrelLogger.InfoOutputWindow("Select a folder first");
return;
}
_droid = selected.Droid;
GetEnumerable().EnumerableChanged += enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable subscribed");
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing && !_disposed)
{
GetEnumerable().EnumerableChanged -= enumerable_EnumerableChanged;
PetrelLogger.InfoOutputWindow("Enumerable unsubscribed (?)");
_droid = null;
_disposed = true;
}
}
IObservableElementEnumerable GetEnumerable()
{
if (_disposed)
throw new ObjectDisposedException("OEEForm");
object obj = DataManager.Resolve(_droid);
IObservableElementEnumerableFactory factory = CoreSystem.GetService<IObservableElementEnumerableFactory>(obj);
IObservableElementEnumerable enumerable = factory.GetEnumerable(obj);
return enumerable;
}
void enumerable_EnumerableChanged(object sender, ElementEnumerableChangeEventArgs e)
{
PetrelLogger.InfoOutputWindow("Enumerable changed");
if (_disposed)
PetrelLogger.InfoOutputWindow("... but I am disposed and unsubscribed!");
}
}
public static class Menu1
{
public static void OEEBegin1_ToolClick(object sender, System.EventArgs e)
{
OEEForm f = new OEEForm();
f.Show();
}
}
}
To replicate:
Run Petrel with the plugin
Load a project with a folder with objects
Select the folder
Activate the plugin menu item
With the popup open, delete an object in the folder
Close the Form popping up
Delete an object in the folder
The message log should clearly show that the event handler is still called after the form is disposed.
You already keep a reference to the underlying enumerable by connecting the event. Events are references as well. Just keep a reference to the enumerable and unsubscribe from the same instance as the one you subscribe to.
To deal with the issue of objects that are deleted by the user you need to listen to the delete event.
I´m writing on an service to watch for the existence different files in diffent folders...
I´m using filesystemwatchers to get the events.
As a part of the deployment one of the watched folders is deleted and new created from time to time.
As a result the service throws an error and is stopped...
Is it possible to catch that kind of error and recreate the filewatcher on the new folder by the service?
Catch the deleted event, and then reschedule with timed poll to watch a new one?
I don't have a compiler to hand right now but I knocked up this pseudo code:
using System;
using System.IO;
public class Watcher : IDisposable{
void Dispose(){ watcher.OnDeleted -= onDelete; }
string file;
FileSystemWatcher watcher;
FileSystemEventHandler onDelete;
public class Watch(string file, FileSystemEventHandler onDelete) {
this.file = file;
watcher = new FileSystemWatcher{ Path = file }
this.OnDelete = onDelete;
watcher.Deleted += onDelete;
watcher.NotifyFilter = ...; // looking for delete event;
// Begin watching.
watcher.EnableRaisingEvents = true;
}
}
public static class watch {
Watcher watcher;
public static void Main() {
watcher = new Watcher("somedir", ondeleted);
SetUpChangeWatchers();
while(true){
// stuff!
}
CleanUpChangeWatchers();
}
private static void ondeleted(object source, RenamedEventArgs e){
CleanUpChangeWatchers();
watcher.Dispose();
while(!directoryRecreated(file)){
Thread.Sleep(...some delay..);
}
SetUpChangeWatchers();
watcher = new Watcher("somedir", ondeleted);
}
}
You can handle this with the .deleted event. However, if you delete the directory assigned to the filesystemwatcher.Path, it may cause an error. One way around this is to assign the parent of the watched directory to filesystemwatcher.Path. Then it should catch the deletion in the .deleted event.
It is also possible to have an error inside the handler if you try to access the directory just deleted. When this happens, you may not get the normal breakpoint and it seems like it's caused by the deletion itself.
I have a question regarding the sequencing of events in the scenario where you are calling a wcf service from silverlight 3 and updating the ui on a seperate thread. Basically, I would like to know whether what I am doing is correct... Sample is as follows. This is my first post on here, so bear with me, because i am not sure how to post actual code. Sample is as follows :
//<summary>
public static void Load(string userId)
{
//Build the request.
GetUserNameRequest request =
new GetUserNameRequest { UserId = userId };
//Open the connection.
instance.serviceClient = ServiceController.UserService;
//Make the request.
instance.serviceClient.GetUserNameCompleted
+= UserService_GetUserNameCompleted;
instance.serviceClient.GetGetUserNameAsync(request);
return instance.VM;
}
/// <summary>
private static void UserService_GetUserNameCompleted(object sender, GetUserNameCompletedEventArgs e)
{
try
{
Controller.UIDispatcher.BeginInvoke(() =>
{
//Load the response.
if (e.Result != null && e.Result.Success)
{
LoadResponse(e.Result);
}
//Completed loading data.
});
}
finally
{
instance.serviceClient.GetUserNameCompleted
-= UserService_GetUserNameCompleted;
ServiceHelper.CloseService(instance.serviceClient);
}
}
So my question basically is, inside of my UI thread when I am loading the response if that throws an exception, will the "finally" block catch that ? If not, should i put another try/catch inside of the lambda where I am loading the response ?
Also, since I am executing the load on the ui thread, is it possible that the finally will execute before the UI thread is done updating ? And could as a result call the Servicehelper.CloseService() before the load has been done ?
I ask because I am having intermittent problems using this approach.
The finally block should get executed before the processing of the response inside the BeginInvoke. BeginInvoke means that the code will get executed in the next UI cycle.
Typically the best approach to this type of thing is to pull all the data you need out of the response and store it in a variable and then clean up your service code. Then make a call to BeginInvoke and update the UI using the data in the variable.