I have implemented a FullEnumerationSimpleSyncProvider that doesn't seem to be handling conflicts.
In the constructor I have set the following properties:
this.Configuration.CollisionConflictResolutionPolicy = CollisionConflictResolutionPolicy.ApplicationDefined;
this.Configuration.ConflictResolutionPolicy = ConflictResolutionPolicy.ApplicationDefined;
this.ItemConstraint += new EventHandler<SimpleSyncItemConstraintEventArgs>(OnItemConstraint);
this.ItemConflicting += new EventHandler<SimpleSyncItemConflictingEventArgs>(OnItemConflicting);
My event handlers for constraints and conflicts:
void OnItemConstraint(object sender, SimpleSyncItemConstraintEventArgs e)
{
e.SetResolutionAction(ConstraintConflictResolutionAction.Merge);
}
void OnItemConflicting(object sender, SimpleSyncItemConflictingEventArgs e)
{
e.SetResolutionAction(ConflictResolutionAction.Merge);
}
However, when I report a conflict in InsertItem() the constraint/conflict event handlers are never invoked.
public override void InsertItem(
object itemData,
IEnumerable<SyncId> changeUnitsToCreate,
RecoverableErrorReportingContext recoverableErrorReportingContext,
out ItemFieldDictionary keyAndUpdatedVersion,
out bool commitKnowledgeAfterThisItem) {
// ...snip...
// Check if it is already there --- name collision
if (itemAlreadyExists)
{
recoverableErrorReportingContext.RecordConstraintError(ConstructDictionary(item.ID));
keyAndUpdatedVersion = null;
commitKnowledgeAfterThisItem = false;
return;
}
// ...snip...
}
I figured when calling RecordConstraintError the sync framework would invoke the appropriate event handler after InsertItem exited.
Any insight would be much appreciated!
If you are handling concurrency conflict and set SetResolutionAction to ConflictResolutionAction.Merge, then your provider need to implement ISimpleSyncProviderConcurrencyConflictResolver Interface (ResolveUpdateUpdateConflict, ResolveLocalDeleteRemoteUpdateConflict, and ResolveLocalUpdateRemoteDeleteConflict.)
If you are handling constraint conflict and set ConstraintConflictResolutionAction to either Merge, RenameDestination or RenameSource, then your provider need to implement ISimpleSyncProviderConstraintConflictResolver interface.
Microsoft Sync Framework Simple Provider – Concurrency Conflict Handling
Related
I expect that uploadImage method finishes once the file is uploaded to AWS, while scanFile method is still running asynchronously in the background;
#RestController
public class EmailController {
#PostMapping("/upload")
#ResponseStatus(HttpStatus.OK)
public void uploadImage(#RequestParam MultipartFile photos) {
awsAPIService.uploadImage(photos);
}
}
...
#Service
public class AwsAPIService {
public void uploadImage(MultipartFile file) {
try {
File fileToUpload = this.convertMultiPartToFile(file);
String fileName = this.generateFileName(file);
s3client.putObject(new PutObjectRequest(AWS_S3_QUARANTINE_BUCKET_NAME,fileName, fileToUpload));
fileToUpload.delete();
// start scan file
scanFile();
} ...
}
#Async
public void scanFile() {
log.info("Start scanning");
String queueUrl = sqs.getQueueUrl("bucket-antivirus").getQueueUrl();
List<Message> messages = sqs.receiveMessage(new ReceiveMessageRequest().withQueueUrl(queueUrl)
.withWaitTimeSeconds(20)).getMessages();
for (Message message : messages) {
// delete message
...
}
}
}
...
#EnableAsync
public class AppConfig {
#Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(2);
taskExecutor.setQueueCapacity(200);
taskExecutor.afterPropertiesSet();
return taskExecutor;
}
}
But this seems still running synchronously. What is the problem here?
By default #Async and other Spring method-level annotations like #Transactional work only on the external, bean-to-bean method call. An internal method call from uploadImage() to scanFile() in the same bean won't trigger the proxy implementing the Spring behaviour. As per Spring docs:
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
You could configure AspectJ to enable annotations on internal method calls, but it's usually easier to refactor the code.
I have a form (which I'll call MainForm) embedded with a TabControl. Every time the user creates a new tab it is filled with an instance of a pre-built Panel (which I'll call MyPanel) which contains many controls.
My MyPanel class has a private variable bool save_state which is set to false every time one of the (editable) controls is edited and set to true when the user "saves" the state of the panel.
I want a visual flag to keep track of tabs that have unsaved changes (e.g. the tab "Tab1" will instead display the text "Tab1 *" if it has unsaved changes). So I want to set up the event handler in my MainForm which can call a method in MyPanel to add the handler to each control.
Since not all my controls use the same EventHandler type (for example, I also need to track DataGridViewRowsAddedEvent, among others), I currently have several methods adding the appropriate handler to the corresponding controls (one for each type of Event Handler), each of which is running the same code (i.e. set the save_state bit to false and append " *" to the tab text.
For example, in MainForm.cpp I have:
#include "MyPanel.h"
void markUnsaved(void) {
// set panel bit to false
// append " *" to tab text if we haven't already
}
void MainForm::handler1(Object ^sender, EventArgs ^e) {
markUnsaved();
}
void MainForm::handler2(Object ^sender, DataGridViewRowsAddedEventArgs ^e) {
markUnsaved();
}
void Main::FormaddNewPanelToTab(int tab_index) {
// check index is valid ...
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->addEventHandlerToControls(gcnew EventHandler(this, &MainForm::handler1));
new_panel->addDgvEventHandlerToControls(gcnew DataGridViewRowsAddedEventHandler(this, &MainForm::handler2));
// rest of code...
}
Though this currently works as intended, this (along with the several other Event Handler types I have to manage) makes my code look really silly.
I am hoping to be able to have have a single event handler in MainForm and a single method in MyPanel which type-casts the Event Handler passed and adds it to all the controls with the appropriate types.
I have tried doing simple casts such as:
void MyPanel::addHandlerToControls(EventHandler ^handler) {
control_NUD->ValueChanged += handler; // this works because ValueChanged is of type EventHandler
control_DGV->RowsAdded += (DataGridViewRowsAddedEventHandler ^)handler; // this compiles but throws an exception
// rest of the code...
}
to no avail.
Any help would be greatly appreciated!
I know this is maybe a bit late for answer but I'd want to show how would I solve this.
Firs of all I suggest to get rid from idea of casting event handlers. Kind of such approach may work in C# (with some adjustments) but as far as I know it's not possible in C++ /CLI.
I'd go for adding new event to a MyPanel class that will be invoked every time when the data on a panel is changed. But to avoid adding a lot of different handlers to a control events in a MyPanel class it's better to create one generic method that will handle all the neccessary control's events and fire new event. Maybe this sounds messy, let me show the code:
public ref class MyPanel
{
// Add a new event
public:
event EventHandler^ DataChanged;
// Add a method that will fire new event
// this methid will be invoked on every control's event that you'll subscribe
private:
generic <typename T>
void DataChangedHandler(System::Object^ sender, T e)
{
// Fire the event
DataChanged(this, EventArgs::Empty);
}
// Once the controls are initialized you may add the event handlers
// I put it in a constructor only for example
MyPanel()
{
control_NUD->ValueChanged += gcnew EventHandler(this, &MyPanel::DataChangedHandler<EventArgs^>);
control_DGV->RowsAdded += gcnew DataGridViewRowsAddedEventHandler(this, &MyPanel::DataChangedHandler<DataGridViewRowsAddedEventArgs^>);
// and so on...
}
}
/// And now in a main form we only need to subscribe to a DataChanged event
public ref class MainForm
{
//...
// the handler
void MyHandler(Object^ sender, EventArgs^ e)
{
markUnsaved();
}
void FormaddNewPanelToTab(int tab_index)
{
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->DataChanged += gcnew EventHandler(this, &MainForm::MyHandler);
}
//...
}
Hope this helps.
I'm rather new to JavaFX8 and facing the following problem. In my current App, which is for document processing/editing, I have two rather expensive tasks. Opening a document and saving a document.
My app has the buttons "import next", "export current" and "export current and import next". For Import and Export, I have two Task of the following structure:
private class Export extends Task<Void> {
public Export() {
this.setOnRunning(event -> {
// do stuff (change cursor etc)
});
this.setOnFailed(event -> {
// do stuff, eg. show error box
});
this.setOnSucceeded(event -> {
// do stuff
});
}
#Override
protected Void call() throws Exception {
// do expensive stuff
return null;
}
}
I submit the task using the Executors.newSingleThreadExecutor();.
For the functionality "export current and import next", my goal is to submit the Export and Import tasks to the executor, but my Import tasks should only run if the export-task was sucessful and the EventHandler given in setOnSucceedded (whichs runs on the GUI thread) finished. If the export fails, it does not make any sense to load the next document because user interaction is needed. How can this be achieved?
First I tired to but the entire logic/error handling in the call method, but this does not work as I cannot change the GUI from this method (i.e. to show an error-box).
As workaround, I'm manually submitting the import-task on the last line of my setOnSucceeded in the export-task, but this is not very flexible, because I want to be sure this task exports only (without subsequent import)...
Don't call the handler property methods setOnXXX in your Task subclass constructor. These actually set a property on the task, so if you also call those methods from elsewhere you will replace the functionality you're implementing in the class itself, rather than add to it.
Instead, override the protected convenience methods:
public class Export extends Task<Void> {
#Override
protected void succeeded() {
super.succeeded();
// do stuff...
}
#Override
protected void running() {
super.running();
// do stuff...
}
#Override
protected void failed() {
super.failed();
// do stuff...
}
#Override
protected Void call() {
// do expensive stuff....
return null ;
}
}
Now you can safely use setOnXXX(...) externally to the Export class without breaking its functionality:
Export export = new Export();
export.setOnSucceeded(e -> {
Import import = new Import();
executor.submit(import);
});
executor.submit(export);
This puts the logic for chaining the tasks at the point where you actually create them, which would seem to be the correct place to do it.
Note that another way to provide multiple handlers for the change of state is to register listeners with the stateProperty():
Export export = new Export();
export.stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
// ...
}
});
From testing, it appears the order of execution of these different mechanisms is:
state listeners
the onSucceeded handler
the Task.succeeded method
All are executed on the FX Application Thread.
So if you want the code in the Task subclass to be executed before the handler added externally, do
public class Export extends Task<Void> {
public Export() {
stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.RUNNING) {
// do stuff
} else if (newState == Worker.State.SUCCEEDED) {
// do stuff
} else if (newState == Worker.State.FAILED) {
// do stuff
}
});
}
#Override
public Void call() {
// ...
}
}
Finally, you could implement the entire logic in your call method: if you need to interact with the UI you can wrap those calls in a Platform.runLater(() -> {});. However, separating the functionality into different tasks as you have done is probably cleaner anyway.
I want to Enable a button from the main form whenever a second form has been closed. I've read something about invokes, but didn't understood much.
How could I achieve this?
When you close the second form, its FormClosed event is automatically raised. Before an event is raised, you can register an event handler to events. This enables you to write code, which is automatically executed, when the event occurrs.
Registering an event handler in .NET is realised by adding a delegate instance to the event. A delegate is a type that describes a method signature. If you instantiate a delegate with gcnew you associate it with a function in your code. You can call the delegate by yourself (which is not needed here) or you can pass it to some other code, which then can invoke it. The latter one is used for events.
For your case that means:
Look at the FormClosed event's delegate type. It is the FormClosedEventHandler which is defined as delegate void FormClosedEventHandler(Object^ sender, FormClosedEventArgs^ e)
This means you must implement a method returning nothing (void) and accepting two arguments: a System::Object and a System::Windows::Forms::FormClosedEventArgs
Instantiate a FormClosedEventHandler delegate and associate it with your method
Register to the FormClosed event on the second form and enable the button in the event handler.
An example:
ref class MainForm
{
...
// event handler function (compatible to the FormClosedEventHandler delegate)
void OnSecondFormClosed(Object^ sender, FormClosedEventArgs^ e)
{
myButton->Enabled = true;
}
void DoSomethingWithSecondForm(Form^ secondForm)
{
// get a disabled Button
myButton->Enabled = false;
// create an event handler by instantiating a delegate
FormClosedEventHandler^ handler = gcnew FormClosedEventHandler(this, &MainForm::OnSecondFormClosed);
// register event handler
secondForm->FormClosed += handler;
}
...
}
(I did not compile the code, but this is how it works in general)
When both involved forms are created from within the same thread, there is no need to do some additional Invoke. Otherwise you must put changes to controls into the same thread that created the control. You can achieve this by passing a delegate to Control::Invoke or Control::BeginInvoke.
// event handler function (compatible to the FormClosedEventHandler delegate)
void OnSecondFormClosed(Object^ sender, FormClosedEventArgs^ e)
{
if (myButton->InvokeRequired)
{
// create a delegate to call the same event handler again
FormClosedEventHandler^ handler = gcnew FormClosedEventHandler(this, &MainForm::OnSecondFormClosed);
// BeginInvoke causes the delegate to be called asynchronously from the UI thread
myButton->BeginInvoke(handler, sender, e);
// nothing to be done here, the actual work happens when the delegate is actually called
return;
}
myButton->Enabled = true;
}
Well I have this code in my Managed C++/Cli in Visual Studio 2008, I want to be able to access the windows forms items inside of the callback of the Thread Function, and I can't, it generates an error.
Is there another way to do that? to be able to modify the GUI stuff inside of a method of the WinForms Class by using the Thread function callback ?
This example shows what I want to do.
I need to use a thread because I want to have the other things in the Forms to be accessible, and without using threads everything just freezes until everything is done, and the "Login" function it calls, takes some time because it does HTTP Requests. and after this HTTP Request I set the values that I got from it in a Form Element.
void Login(){
this->btn_next->Enabled = false;
this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
if(this->clb_contas->CheckedItems->Count <= 0){
//...
}
}
System::Void test_login_Click(System::Object^ sender, System::EventArgs^ e) {
ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::Login);
Thread^ t = gcnew Thread(start);
t->Start();
}
Does anybody know how could I do that? if you think this can't be done and you want to suggest something something to make the GUI available while doing the process, I'm open for suggestions.
I hope I was being clear enough.
Thanks in advance.
All UI related code should be executed on the UI thread. In your case, that means that only the code you denoted with //... should be run on a separate thread. Extract that long-running code in its own method and pass that method to ThreadStart instead of Login(). Then you'll need to arrange for a way for the worker thread to notify the UI thread if and when it's complete.
Update:
Here's a crude example of how to modify your code. I would prefer to extract the long running operation in its own class if it is of sufficient complexity, but I think you get the idea.
The call to BeginInvoke ensures that LongRunningOperationComplete will be executed on the form's UI thread. You can use the same approach to call other methods that update the UI to indicate progress, even while the time-consuming operation is still running. If those methods require more parameters, you can create different delegates with the appropriate signature, and pass those parameters in the call to BeginInvoke. See here for how to do that.
// Same signature as LongRunningOperationComplete
delegate void MyInvokeDelegate();
void LongRunningOperation() {
for (int i=0; i < 100; i++) {
Thread::Sleep(100);
// The actual work that you're doing
}
// Operation complete. Update UI.
this->BeginInvoke(gcnew MyInvokeDelegate(this, &Login_Test::LongRunningOperationComplete));
}
void LongRunningOperationComplete() {
this->btn_next->Enabled = true;
this->login_accounts_facebook->Enabled = true;
}
System::Void StartMyLongRunningOperation() {
ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::LongRunningOperation);
Thread^ t = gcnew Thread(start);
t->Start();
}
void Login() {
this->btn_next->Enabled = false;
this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
if(this->clb_contas->CheckedItems->Count <= 0){
StartMyLongRunningOperation();
}
}
System::Void test_login_Click(System::Object^ sender, System::EventArgs^ e) {
Login();
}