suppress events for Flex objects - apache-flex

[Edit]
The main question here loosely translates as 'is Flex multi-threaded'? I have since found out that it is not, so I won't have data mysteriously changing half way through an operation. The code below worked, but made things awkward and confusing. I eventually fixed the problem with an architecture change, eliminating the need to suppress events. As the first commenter suggested.
Infinite loops were eliminated by changing the way events were listened to and performing certain actions explicitly rather than via events.
Collating events was made easier using a command pattern.
Basically, do not use the code below if you come across this page!
[/Edit]
I'm building some Flex applications using a simple, lightweight MVC pattern. Models extend or encapsulate a dispatcher and fire events when updated. I'm stuck with Flex 3.5.
In some situations, I'll want to suppress these events to avoid infinite loops or help collate multiple actions into a single event.
My first stab at a solution that doesn't litter the models with unnecessary and confusing code is this:
private var _suppressEvents:Boolean = false;
public function suppressEvents(callback:Function):void
{
// In case of error, ensure the suppression is turned off, then re-throw
var err:Error = null;
_suppressEvents = true;
try
{
callback();
}
catch(e:Error)
{
err = e;
}
_suppressEvents = false;
if (err)
{
throw (err);
}
}
public function dispatch(type:String, data:*):void
{
// Suppress if called from a suppress callback.
if (!_suppressEvents)
{
_dispatcher.dispatchEvent(new DataEvent(type, data));
}
}
Obviously I call 'suppressEvents' with a function containing the model code I wish to run.
My questions:
1: Is there a chance I could accidentally lose events using this technique?
2: Do I need to think about any other error edge cases when it comes to ensuring I don't accidentally end up in a suppressed state after a call?
3: Is there a cleaner way I'm missing?
Thanks!

Related

Validation errors block undo

I have the following problem with my EMF based Eclipse application:
Undo works fine. Validation works fine. But when there is a validation error for the data in a GUI field, this blocks the use of the undo action. For example, it is not possible to undo to get back to a valid state for that field.
In this picture it is not possible to use undo:
Tools that are used in the application:
Eclipse data binding
UpdateValueStrategys on the bindings for validation
Undo is implemented using standard UndoAction that calls CommandStack.undo
A MessageManagerSupport class that connects the validation framework to the Eclipse Forms based GUI.
The data bindings look like this:
dataBindingContext.bindValue(WidgetProperties.text(...),
EMFEditProperties.value(...), validatingUpdateStrategy, null);
The problem is this:
The undo system works on the commands that change the model.
The validation system stops updates from reaching to model when there are validation errors.
To make undos work when there are validation errors I think I could do one of these different things:
Make undo system work on the GUI layer. (This would be a huge change, it's probably not possible to use EMF for this at all.)
Make the invalid data in the GUI trigger commands that change the model data, in the same way as valid data does. (This would be okay as long as the data can not be saved to disk. But I can't find a way to do this.)
Make the validation work directly on the model, maybe triggered by a content listener on the Resource. (This is a big change of validation strategy. It doesn't seem possible to track the source GUI control in this stage.)
These solutions either seem impossible or have severe disadvantages.
What is the best way to make undo work even when there are validation errors?
NOTE: I accept Mad Matts answer because their suggestions lead me to my solution. But I'm not really satisfied with that and I wish there was a better one.
If someone at some time finds a better solution I'd be happy to consider to accept it instead of the current one!
It makes sense that the Validator protects your Target value from invalid values.
Therefor the target commandstack remains untouched in case of an invalid value.
Why would you like to force invalid values being set? Isn't ctrl + z in the GUI enough to reset the last valid state?
If you still want to set these values to your actual Target model, you can play around with the UpdateValueStrategy.
The update phases are:
Validate after get - validateAfterGet(Object)
Conversion - convert(Object)
Validate after conversion - validateAfterConvert(Object)
Validate before set - validateBeforeSet(Object)
Value set - doSet(IObservableValue, Object)
I'm not sure where the validation error (Status.ERROR) occurs exactly, but you could check where and then force a SetCommand manually.
You can set custom IValidator for each step to your UpdateValueStrategy to do that.
NOTE: This is the solution I ended up using in my application. I'm not really satisfied with it. I think it is a little bit of a hack.
I accept Mad Matts answer because their suggestions lead me to this solution.
If someone at some time finds a better solution I'd be happy to consider to accept it instead of the current one!
I ended up creating an UpdateValueStratety sub-class which runs a validator after a value has been set on the model object. This seems to be working fine.
I create this answer to post the code I ended up using. Here it is:
/**
* An {#link UpdateValueStrategy} that can perform validation AFTER a value is set
* in the model. This is used because undo dosen't work if no model changed in made.
*/
public class LateValidationUpdateValueStrategy extends UpdateValueStrategy {
private IValidator afterSetValidator;
public void setAfterSetValidator(IValidator afterSetValidator) {
this.afterSetValidator = afterSetValidator;
}
#Override
protected IStatus doSet(IObservableValue observableValue, Object value) {
IStatus setStatus = super.doSet(observableValue, value);
if (setStatus.getSeverity() >= IStatus.ERROR || afterSetValidator == null) {
return setStatus;
}
// I used a validator here that calls the EMF generated model validator.
// In that way I can specify validation of the model.
IStatus validStatus = afterSetValidator.validate(value);
// Merge the two statuses
if (setStatus.isOK() && validStatus.isOK()) {
return validStatus;
} else if (!setStatus.isOK() && validStatus.isOK()) {
return setStatus;
} else if (setStatus.isOK() && !validStatus.isOK()) {
return validStatus;
} else {
return new MultiStatus(Activator.PLUGIN_ID, -1,
new IStatus[] { setStatus, validStatus },
setStatus.getMessage() + "; " + validStatus.getMessage(), null);
}
}
}

How to detect whether a child_added event is local?

In a web app I have this:
function onChildAdded(snapshot) {
// ...
}
someFirebaseLocation.on('child_added', onChildAdded);
I'm looking for a 100% reliable way to detect whether the child_added event is immediate, so that I can handle the two cases correctly: when after push() the function gets called immediately (sync) vs when the function gets called async.
Setting a flag before the push() call is not reliable I think. (Potential race condition when an async event comes in, and the flag might not get reset when there's an error).
Another option would be
var pushed = push(...);
and then in child_added
if (snap.name() === pushed)
but an incoming message could have the same .name() thus there could be collisions. The probability of a clash is debatable, but I'd prefer a simple and watertight way to get the info.
It would be great if I could do this:
function onChildAdded(snapshot, prevChildName, isImmediateEvent) {
if (isImmediateEvent) {
// Handle as sync event.
} else {
// Handle as async event.
}
}
someFirebaseLocation.on('child_added', onChildAdded);
or this
function onChildAdded(snapshot, prevChildName) {
if (snapshot.isFromImmediateEvent) {
// Handle as sync event.
} else {
// Handle as async event.
}
}
someFirebaseLocation.on('child_added', onChildAdded);
Is there some other reliable option? Otherwise I'll ask the Firebase guys whether they could generally pass a bool "isImmediateEvent" into the callback (after snapshot,prevChildName).
Tobi
You've covered the two options for now and either one should work reliably (see notes below). We might add features in the future to make this easier, but nothing concrete is planned at this point.
A couple notes:
Setting a flag should work fine. No async events will happen until after your synchronous code has finished running. You can avoid the error issue by using a try/finally block to reset it.
push() id's are designed to be universally unique, so you really shouldn't worry about conflicts.

Any side-effects using SqlParameterCollection.Clear Method?

I have this specific situation where I need to execute a stored procedure 3 times before I declare it failed. Why 3 times because to check if a job that was started earlier finished. I am going to ask a separate question for deciding if there is a better approach. But for now here is what I am doing.
mysqlparametersArray
do{
reader = MyStaticExecuteReader(query,mysqlparametersArray)
Read()
if(field(1)==true){
return field(2);
}
else{
//wait 1 sec
}
}while(field(1)==false);
MyStaticExecuteReader(query,mysqlparametersArray)
{
//declare command
//loop through mysqlparametersArray and add it to command
//ExecuteReader
return reader
}
Now this occasionally gave me this error:
The SqlParameter is already contained by another
SqlParameterCollection.
After doing some search I got this workaround to Clear the parameters collection so I did this:
MyStaticExecuteReader(query,mysqlparametersArray)
{
//declare command
//loop through mysqlparametersArray and add it to command Parameters Collection
//ExecuteReader
command.Parameters.Clear()
return reader
}
Now I am not getting that error.
Question: Is there any side-effect using .Clear() method above?
Note: Above is a sample pseudo code. I actually execute reader and create parameters collection in a separate method in DAL class which is used by others too. So I am not sure if making a check if parameters collection is empty or not before adding any parameters is a good way to go.
I have not ran into any side effects when I have used this method.
Aside from overhead or possibly breaking other code that is shared, there is no issue with clearing parameters.

Asynchronous Callback in GWT - why final?

I am developing an application in GWT as my Bachelor's Thesis and I am fairly new to this. I have researched asynchronous callbacks on the internet. What I want to do is this: I want to handle the login of a user and display different data if they are an admin or a plain user.
My call looks like this:
serverCall.isAdmin(new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
//display error
}
public void onSuccess(Boolean admin) {
if (!admin){
//do something
}
else{
//do something else
}
}
});
Now, the code examples I have seen handle the data in the //do something// part directly. We discussed this with the person who is supervising me and I had the idea that I could fire an event upon success and when this event is fired load the page accordingly. Is this a good idea? Or should I stick with loading everything in the inner function? What confuses me about async callbacks is the fact that I can only use final variables inside the onSuccess function so I would rather not handle things in there - insight would be appreciated.
Thanks!
Since the inner-class/ anonymous function it is generated at runtime it needs a static memory reference to the variables it accesses. Putting final to a variable makes its memory address static, putting it to a safe memory region. The same happens if you reference a class field.
Its just standard java why you can only use Final variables inside an inner-class. Here is a great discussion discussing this topic.
When I use the AsyncCallback I do exactly what you suggested, I fire an event though GWT's EventBus. This allows several different parts of my application to respond when a user does log in.

What can cause event-handling closures to stop working?

I'll try to be as concise as possible. I have a number of objects in an array, and I'm applying event listeners to each one using closures:
//reduced to the logic in question:
buttons.forEach(function(button:EventDispatcher, i:int, list:Array):void {
button.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void {
button.filters = [button_glow_filter];
});
});
//button-specific click handlers:
buttons[0].addEventListener(MouseEvent.MOUSE_CLICK, handle_some_action);
This works perfectly for a while, until I perform an unrelated action on the UI. It's a very complex system, so I'm not really sure what is happening. I can confirm that the unrelated action has no direct effect on the object that contains the buttons or the buttons themselves (at least, it's not changing anything via the public interfaces). The buttons still exist, and the click event listeners still work correctly because those are individually assigned real functions on the class's interface.
My question therefore is: does anyone know what can cause these closures to stop handling the MouseOver events without having any other perceptible effect on the related objects?
There are a number of ways to accomplish this MouseOver behavior, and for now I've switched to one that works, but I'd still like to know the answer to this question for future reference.
I figured out the likely culprit almost immediately after posting: garbage collection. It took just a couple of minutes to confirm. This is exactly what the useWeakReference parameter is for in the addEventListener interface; it defaults to true. By setting it to false, it prevents listeners assigned in this fashion from being garbage collected.
The correct code is:
buttons.forEach(function(button:EventDispatcher, i:int, list:Array):void {
button.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void {
button.filters = [button_glow_filter];
}, false, 0, false);
});

Resources