CompletionStage / read-only CompletableFuture / design-by-UOE ?

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

CompletionStage / read-only CompletableFuture / design-by-UOE ?

Christian Schudt
Hi,

I want to design an API which returns „read-only“ CompletableFutures, because I don’t want the API user to be able to complete the future. Instead only internal triggers of my library should complete it.
(Same requirement as in [1]).

After some research, the preferred way to achieve this, seems to be to simply return CompletionStage, which is kind of the read-only interface.
However, a user could still complete it by calling: toCompletableFuture().complete(...);

To prevent this, I throw an UnsupportedOperationException in toCompletableFuture(), in accordance with the JavaDoc.

However, composing/chaining multiple CompletionStages then no longer works, because they internally call toCompletableFuture().
JavaDoc says it does not "interoperate with others“ then, but is that really necessary?


The JDK 9 JavaDocs [2] have an example which subclasses CompletableFuture, which „disables“ some the „complete“ methods by throwing UOE.
This post [3] also suggests a "design-by-UOE“, but still I am feeling uncomfortable with it, I’d rather return the minimal CompletionStage.

What would be the preferred way to do this?

class MyReadOnlyCompletableFuture extends CompletableFuture {
        // override all complete methods and throw UOE
}

class MyCompletionStage implements CompletionStage {
        // delegate all methods to instance of MyReadOnlyCompletableFuture
}

Then return instances of MyCompletionStage in API methods.


But then the next problem: JDK 9 added new methods to complete a CompletableFuture. Those won’t be overridden, when compiled for JDK 8.
(completeOnTimeout(), completeAsync(), ..)
API users could then still complete a read-only CompletionStage:

myCompletionStage.toCompletableFuture(). completeAsync(…) when using JDK 8 libraries under JDK 9.


Btw: I also feel CompletableFuture.allOf(…); should accept CompletionStages as parameter. Thoughts?

Thanks for some feedback for a good design.

Best regards,
Christian


[1]: http://cs.oswego.edu/pipermail/concurrency-interest/2014-February/012354.html
[2]: http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/CompletableFuture.html
[3]: http://cs.oswego.edu/pipermail/concurrency-interest/2013-July/011496.html
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletionStage / read-only CompletableFuture / design-by-UOE ?

Josh Humphries
Instead of throwing UnsupportedOperationException in toCompletableFuture(), you could just return a new future (that completes with the same result or failure when the receiver completes).

That way the original CompletionStage is still read-only since toCompletableFuture().complete() would end up modifying a different object.

This is actually necessary for implementations of CompletionStage that do not actually extend CompletableFuture but implement the API from scratch (which I've done to inter-op between frameworks that use Guava ListenableFutures and Java 8 CompletionStage APIs).




----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  678-400-4867

On Tue, Jan 26, 2016 at 3:42 PM, Christian Schudt <[hidden email]> wrote:
Hi,

I want to design an API which returns „read-only“ CompletableFutures, because I don’t want the API user to be able to complete the future. Instead only internal triggers of my library should complete it.
(Same requirement as in [1]).

After some research, the preferred way to achieve this, seems to be to simply return CompletionStage, which is kind of the read-only interface.
However, a user could still complete it by calling: toCompletableFuture().complete(...);

To prevent this, I throw an UnsupportedOperationException in toCompletableFuture(), in accordance with the JavaDoc.

However, composing/chaining multiple CompletionStages then no longer works, because they internally call toCompletableFuture().
JavaDoc says it does not "interoperate with others“ then, but is that really necessary?


The JDK 9 JavaDocs [2] have an example which subclasses CompletableFuture, which „disables“ some the „complete“ methods by throwing UOE.
This post [3] also suggests a "design-by-UOE“, but still I am feeling uncomfortable with it, I’d rather return the minimal CompletionStage.

What would be the preferred way to do this?

class MyReadOnlyCompletableFuture extends CompletableFuture {
        // override all complete methods and throw UOE
}

class MyCompletionStage implements CompletionStage {
        // delegate all methods to instance of MyReadOnlyCompletableFuture
}

Then return instances of MyCompletionStage in API methods.


But then the next problem: JDK 9 added new methods to complete a CompletableFuture. Those won’t be overridden, when compiled for JDK 8.
(completeOnTimeout(), completeAsync(), ..)
API users could then still complete a read-only CompletionStage:

myCompletionStage.toCompletableFuture(). completeAsync(…) when using JDK 8 libraries under JDK 9.


Btw: I also feel CompletableFuture.allOf(…); should accept CompletionStages as parameter. Thoughts?

Thanks for some feedback for a good design.

Best regards,
Christian


[1]: http://cs.oswego.edu/pipermail/concurrency-interest/2014-February/012354.html
[2]: http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/CompletableFuture.html
[3]: http://cs.oswego.edu/pipermail/concurrency-interest/2013-July/011496.html
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletionStage / read-only CompletableFuture / design-by-UOE ?

Viktor Klang
+1 for Josh's proposal

On Tue, Jan 26, 2016 at 10:38 PM, Josh Humphries <[hidden email]> wrote:
Instead of throwing UnsupportedOperationException in toCompletableFuture(), you could just return a new future (that completes with the same result or failure when the receiver completes).

That way the original CompletionStage is still read-only since toCompletableFuture().complete() would end up modifying a different object.

This is actually necessary for implementations of CompletionStage that do not actually extend CompletableFuture but implement the API from scratch (which I've done to inter-op between frameworks that use Guava ListenableFutures and Java 8 CompletionStage APIs).




----
Josh Humphries
Manager, Shared Systems  |  Platform Engineering
Atlanta, GA  |  <a href="tel:678-400-4867" value="+16784004867" target="_blank">678-400-4867

On Tue, Jan 26, 2016 at 3:42 PM, Christian Schudt <[hidden email]> wrote:
Hi,

I want to design an API which returns „read-only“ CompletableFutures, because I don’t want the API user to be able to complete the future. Instead only internal triggers of my library should complete it.
(Same requirement as in [1]).

After some research, the preferred way to achieve this, seems to be to simply return CompletionStage, which is kind of the read-only interface.
However, a user could still complete it by calling: toCompletableFuture().complete(...);

To prevent this, I throw an UnsupportedOperationException in toCompletableFuture(), in accordance with the JavaDoc.

However, composing/chaining multiple CompletionStages then no longer works, because they internally call toCompletableFuture().
JavaDoc says it does not "interoperate with others“ then, but is that really necessary?


The JDK 9 JavaDocs [2] have an example which subclasses CompletableFuture, which „disables“ some the „complete“ methods by throwing UOE.
This post [3] also suggests a "design-by-UOE“, but still I am feeling uncomfortable with it, I’d rather return the minimal CompletionStage.

What would be the preferred way to do this?

class MyReadOnlyCompletableFuture extends CompletableFuture {
        // override all complete methods and throw UOE
}

class MyCompletionStage implements CompletionStage {
        // delegate all methods to instance of MyReadOnlyCompletableFuture
}

Then return instances of MyCompletionStage in API methods.


But then the next problem: JDK 9 added new methods to complete a CompletableFuture. Those won’t be overridden, when compiled for JDK 8.
(completeOnTimeout(), completeAsync(), ..)
API users could then still complete a read-only CompletionStage:

myCompletionStage.toCompletableFuture(). completeAsync(…) when using JDK 8 libraries under JDK 9.


Btw: I also feel CompletableFuture.allOf(…); should accept CompletionStages as parameter. Thoughts?

Thanks for some feedback for a good design.

Best regards,
Christian


[1]: http://cs.oswego.edu/pipermail/concurrency-interest/2014-February/012354.html
[2]: http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/CompletableFuture.html
[3]: http://cs.oswego.edu/pipermail/concurrency-interest/2013-July/011496.html
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest




--
Cheers,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletionStage / read-only CompletableFuture / design-by-UOE ?

Christian Schudt
In reply to this post by Josh Humphries
Nice, that sounds like a good and working solution :-), thanks.

I’ve did it like that (in a CompletionStage implementation which hides a private CompletableFuture):

@Override
public CompletableFuture<T> toCompletableFuture() {
    return completableFuture.whenComplete((result, throwable) -> {
    });
}

If there’s a better way, let me know.

Best,
Christian



> Am 26.01.2016 um 22:38 schrieb Josh Humphries <[hidden email]>:
>
> Instead of throwing UnsupportedOperationException in toCompletableFuture(), you could just return a new future (that completes with the same result or failure when the receiver completes).
>
> That way the original CompletionStage is still read-only since toCompletableFuture().complete() would end up modifying a different object.
>
> This is actually necessary for implementations of CompletionStage that do not actually extend CompletableFuture but implement the API from scratch (which I've done to inter-op between frameworks that use Guava ListenableFutures and Java 8 CompletionStage APIs).
>
>
>
>
> ----
> Josh Humphries
> Manager, Shared Systems  |  Platform Engineering
> Atlanta, GA  |  678-400-4867
> Square (www.squareup.com)
>
> On Tue, Jan 26, 2016 at 3:42 PM, Christian Schudt <[hidden email]> wrote:
> Hi,
>
> I want to design an API which returns „read-only“ CompletableFutures, because I don’t want the API user to be able to complete the future. Instead only internal triggers of my library should complete it.
> (Same requirement as in [1]).
>
> After some research, the preferred way to achieve this, seems to be to simply return CompletionStage, which is kind of the read-only interface.
> However, a user could still complete it by calling: toCompletableFuture().complete(...);
>
> To prevent this, I throw an UnsupportedOperationException in toCompletableFuture(), in accordance with the JavaDoc.
>
> However, composing/chaining multiple CompletionStages then no longer works, because they internally call toCompletableFuture().
> JavaDoc says it does not "interoperate with others“ then, but is that really necessary?
>
>
> The JDK 9 JavaDocs [2] have an example which subclasses CompletableFuture, which „disables“ some the „complete“ methods by throwing UOE.
> This post [3] also suggests a "design-by-UOE“, but still I am feeling uncomfortable with it, I’d rather return the minimal CompletionStage.
>
> What would be the preferred way to do this?
>
> class MyReadOnlyCompletableFuture extends CompletableFuture {
>         // override all complete methods and throw UOE
> }
>
> class MyCompletionStage implements CompletionStage {
>         // delegate all methods to instance of MyReadOnlyCompletableFuture
> }
>
> Then return instances of MyCompletionStage in API methods.
>
>
> But then the next problem: JDK 9 added new methods to complete a CompletableFuture. Those won’t be overridden, when compiled for JDK 8.
> (completeOnTimeout(), completeAsync(), ..)
> API users could then still complete a read-only CompletionStage:
>
> myCompletionStage.toCompletableFuture(). completeAsync(…) when using JDK 8 libraries under JDK 9.
>
>
> Btw: I also feel CompletableFuture.allOf(…); should accept CompletionStages as parameter. Thoughts?
>
> Thanks for some feedback for a good design.
>
> Best regards,
> Christian
>
>
> [1]: http://cs.oswego.edu/pipermail/concurrency-interest/2014-February/012354.html
> [2]: http://gee.cs.oswego.edu/dl/jsr166/dist/docs/java/util/concurrent/CompletableFuture.html
> [3]: http://cs.oswego.edu/pipermail/concurrency-interest/2013-July/011496.html
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletionStage / read-only CompletableFuture / design-by-UOE ?

thurstonn
In reply to this post by Christian Schudt
"...I’d rather return the minimal CompletionStage"

Well, there's the 1.9 API method CompletableFuture#minimalCompletionStage() (perhaps that's exactly what you meant); you could just delegate to that in your overridden #toCompletableFuture() (casting it to a CompletableFuture of course).
That returned CF instance seems to give you exactly the behavior you want