2

To begin with I am not sure how to express the issue but I am really stuck (I read this Why can I still make changes to this final variable? but it didn't help). I use CodenameOne framework but I am not sure too the issue spurts from it, it could be general java.

In my App I am sending Reports to the server. It works great.

The method is as follows in the Report class:

    public void sendAsync(SuccessCallback<Report> onSuccess) {
      // Update : With a shallow copy it does not work
     // final Report currentReport = this;

     // But with a deeper copy it does
     final Report currentReport = clone(this);

     final MultipartRequest request = new MultipartRequest() {

        @Override
        protected void readResponse(InputStream input) throws IOException { 
        // ... Does stuff here on Report's attributes

        // Here the variable currentReport changes during overlapping sendings

      @Override
      postResponse(){

        // The goal is to use some attributes from the current report
        // to be shown in a popup to remind the user what we sent
        // BUT the as written above the currentReport changes during 
        // overlapping sendings so that what is shown to the user only 
        // deals with the lastest report and does not make sense.

        onSuccess(currentReport);

      }


     }
    } // end of sendAsync

I call it like this when the user presses the send button:

 Report myReport = new Report();
 myReport.photoPath.set(...);
 myReport.id.set(...);
 anotherMethod(myReport); // this passes myReport to sendAsync but never modifies it  
 // Here myReport has still the right attributes based on the parameters
 myReport.sendAsync((sentReport) -> {/* does something else */});

The problem is that when the user presses the send button whereas a previous sending is going on, the previous sending takes the attributes of the following one and it is NOT what I want.

So I don't know where to search in my code, the variables are not static. How can I make previous currentReport not change when the sendings overlap ?

February 25th 2018's update

What I don't understand is why I have to create a deep copy of this to make it work (the static Report clone(Report sourceReport) method creates a new Report, sets its attributes with the ones of the passed in sourceReport, and returns the created Report) ?

Moreover when the user presses the button the following is done :

 Report report = new Report();
 report.photoPath.set(the_path_returned_from_the_camera);
 report.otherAttributes.set(values);

 report.sendAsync((sentReport) -> {

     showConfirmationPopup(sentReport);

     // However here report.photoPath returns a different path
     // from sentReport.photoPath which surprises me. Why do they differ ?

     System.out.println(report.photoPath.get()); //prints /home/a.jpg
     System.out.println(sentReport.photoPath.get()); //prints /home/b.jpg

  });

What I don't understand too is why within the lambda, report and sendReport do not have the same content when a deep copy is done (there were the same with a shallow copy) ?

The Report constructor works like :

 public Report() {
    this.location.set(null);
    this.photoPath.set("");  
     ...
    this.connectionError = false;
}

Thank you very much in advance, I can provide more details if requested

HelloWorld
  • 2,275
  • 18
  • 29
  • is a `new Report` being created every time the user presses the button? where are you getting `parameters`? – Andrew Tobilko Feb 24 '18 at 11:46
  • also, I don't get this line `final Report currentReport = this;` – Andrew Tobilko Feb 24 '18 at 11:47
  • @AndrewTobilko Thanks for asking! Yes a new Report is created every time the user presses the button. The parameters are retrieved at different stage in the process. They are actually set like this : myReport.id.set(...). If I print myReport before calling sendAsync it contains the right attributes. With the line you quoted I "save" the current report to use in a callback later in the sendAsync method. The callback has a form of a lambda and I need to supply the callback with the currentReport so that its photo can be used in a confirmation message when it has been successfuly sent. – HelloWorld Feb 24 '18 at 20:53

2 Answers2

1

In Java. there is no built-in way to identify which method modify an object or not and thus no way to block those (ex: no equivalent to C++ "const methods"). However, there are design patterns to achieve what you are trying to do, depending on the specifics.

Immutable: simply make an object without setters so it cannot be modified after creation; the reference can still be changed (unless final) however.

Memento (a variant of at least): basically take a snapshot/copy of the object when the event is fired so any modifications being done to the original object after the event is fired doesn't impact the ones from event already fired. Be careful tho, you might need some kind of "deep-clone", if your object contains any other object, doint obj1 = obj2 will only make the two reference the same instance which can still be modified.

Another option around your issue could simply be to prevent event re-submission until the previous event finished (successfully or with Exception/failure.

Simon Berthiaume
  • 643
  • 4
  • 11
  • While C++ has `const`, Java has `final` – GalAbra Feb 24 '18 at 12:06
  • C++'s const is not EXACTLY the same as Java's final. For example, in C++ a const method means it cannot modify any non-mutable member of the class, with a const object you cannot call non-const method in C++ so it also means that if your method takes in a "const" parameter, it indicates your method has no intention to modify that object."Const" is the C++ concept I miss most in Java since whenever you pass an object to a method in Java, you can never be sure what is going to be done to it. There is a lot to read about [const-correctness](https://isocpp.org/wiki/faq/const-correctness). – Simon Berthiaume Feb 24 '18 at 12:27
  • Thanks @SimonBerthiaume. It works if I prevent re-submission, but it is not user friendly (user has to wait), I prefer to let them the ability to report as quick as they want. What I don't understand is that the method is not static nor is the variable currentReport but it still changes. Shouldn't method variables be independent in one call from another one ? I don't know where to look at. Regarding the immutable pattern, I use BusinessProperty (kind of beans) with builtin setters and getters so they are inherently mutable. – HelloWorld Feb 24 '18 at 21:11
  • @HelloWorld The problem might partially be because of `final Report currentReport = this;`. That statement only copies a reference to `this`, it doesn't copy the object itself, it means that if `this` is modified, so will `currentReport`, it has nothing to do with static or not. – Simon Berthiaume Feb 25 '18 at 05:11
  • @SimonBerthiaume I agree with the first part of your comment. But I create new instances of Report so in each one `this` shouldn't be modified, should it ? You must be right because creating a deep copy makes the whole thing work although I don't understand why (I'll update the original post). – HelloWorld Feb 25 '18 at 05:23
  • @HelloWorld Just realized you did share that part of your code, sorry. Can you share the Report constructor code? My guess is, within it, you might just be copying references and thus it can keep on changing. – Simon Berthiaume Feb 25 '18 at 12:41
1

Every invocation of the method creates a new stack frame and places the variable within hence you have a "new final" object. Both are unmodifiable but both are new.

From reading your description I think you are looking at the wrong thing. I suggest placing a breakpoint on the point in which you invoke addToQueue and also look at the details in the network monitor to gain insight into what is happening in the code. My guess is that you are getting two requests one after the other and your server only accepts the last one due to application state.

Shai Almog
  • 51,749
  • 5
  • 35
  • 65
  • Thanks @Shai for your comment. What is sent to the server looks correct (two requests with different contents in the Network Monitor), for exemple the images of the different reports are also different. What is not is the Report I get in the OnSuccess callback that is processed by the postResponse() method (I'll update my first post to show this). There the Report changes so when I show a popup with the report image, it always shows the latest report image! – HelloWorld Feb 25 '18 at 05:09
  • If you show a popup using the Dialog API notice that this is a blocking call and might distort the order of event processing. – Shai Almog Feb 25 '18 at 06:48
  • if they were out of order would not matter. However the same report image is shown in all popup (denoting the same Report is used). – HelloWorld Feb 25 '18 at 15:32
  • You are doing `sendAsync` then changing the state of the object while waiting for the popup dialog's invoke and block. The blocking dialog stops event from processing on the current sequence of events which means they will only happen after it's finished. So once the dialog is disposed the original events that were queued behind it process and impact the results you are seeing. You can prove that theory by using a modless dialog instead or wrapping your popup with a `callSerially`. – Shai Almog Feb 26 '18 at 03:37
  • Sorry @Shai my previous comment was ill written. You're right it can happen that the order of event processing gets distorted. I just meant it is not a problem in the app, as long as what is displayed is correct. – HelloWorld Feb 26 '18 at 04:39
  • OK, in that case it would be hard to say what went wrong without looking at the full code in a debugger. – Shai Almog Feb 27 '18 at 04:09