-
Notifications
You must be signed in to change notification settings - Fork 166
[WIP] Allow POJOs as CloudEvent data #198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Allow POJOs as CloudEvent data #198
Conversation
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
| * @throws IllegalArgumentException if there isn't any unmarshaller from the inner data to T | ||
| */ | ||
| @Nullable | ||
| <T> T getData(Class<T> c) throws IllegalArgumentException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as seen in Jackson and others, Class may not represent all the types (e.g. List<User>). Consider using a type reference instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that, but that means we need to define our type reference? I'm not sure i wanna dive in into that complexity... And if we define our type reference, then the EventDataCodec implementer needs to also convert our type reference to its type reference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea for using Class is:
- For the simple use cases, the user just uses
getData(c). - For the user cases when the user needs a type reference, he can just use jackson directly: he gets the raw payload with
getData(Object.class)and then he uses the jackson mapper to map it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am personally against this operation all together regardless of Type or Class. CloudEvent type is representation of the actual cloud event on the wire. It should be as simple and as light as possible containing only essentials. We had a very long and contentious debate about it, so let's not go back to square one.
If user needs stronger type for its data representation then there should be another abstraction that would perform such conversion, but only as an option.
Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
|
@slinkydeveloper previously, CloudEvent was a POJO, but now it may need to store a reference to the encoder/decoder service to perform Also, obviously Another question is how
In Liiklus, I went with option number 2 for performance reasons, but I can't say I am 100% happy about the API of it :) Curious to hear your thoughts on these. |
|
I think i didn't underlined the important prerequisite of this PR, as you sad:
If it's a CloudEvent, it must be somehow encodable/decodable to bytes. If you put inside the data field of I see this feature more as a mix of optimization and sugar to use the sdk, but it's not my intention to break the existing assumptions/requirements of
|
So Of course, an implementation can always fail on |
As we previously figured,
|
|
Another question to ask: does it really need to be in the API at all. Since it is an optimization, it can as well be implementation specific, when the optimization can be made. |
I don't like the idea of bringing back generics to |
It needs to be an API in the sense that, if I create an in memory |
For that, you could have
This sounds too magical for me given that we're talking about the thin interface in the API module. Think of this interface as the one that defines interoperability between various CloudEvent implementations. You can have as many features as desired in the core module (aka the "default" implementation), but the interface needs to be carefully reviewed to not block other implementations nor make them suffer working around some "features" of it that they can't support. |
Still, that's a prior assumption that the payload is json 😄 In sdk-go we have this interesting api usability feature that you can decode the payload without checking/having knowledge of the data content type: |
Funny enough, |
|
How does it sound the option of |
|
@slinkydeveloper I like it, but I'd still bring back the generic parameter, to avoid unnecessary casting when the type is clear. It will always allow to have
Yeah, this definitely belongs to SmartCloudEvent<T> extends CloudEvent<T> {
<T> T readData(Class<T> type);
}(the name is random and there must be a better one, of course, just I didn't want to spend hours trying to find it 😂 ) |
|
Re "naive implementation" - it will indeed always return |
|
Thanks for looking into this. My use case is that I want to store CloudEvent's in MongoDB but hopefully provide a way to avoid double encoding/decoding of the body (see discussion here for context). This means that I'd like to be able to set an instance of I kind of like the non-generic api better though (as compared with cloud-events 1.3), it looks less complex and you know that you're dealing with
I don't really understand what you mean here (I may be corrupted by On a different note I would strongly suggest to keep encoding/decoding/codecs out of the CloudEvent implementation. In my mind the data representation of a cloud event and how its encoded/decoded are different concerns. Better to pass |
|
FWIW, I believe the question of "how should CloudEvent objects represent data" is likely to be an important one across multiple languages. I've been thinking about this myself quite a lot for the C# SDK, but without any concrete progress yet. |
|
@jskeet Can't agree with you more. It is certainly an important aspect and must cover many angles of type representation and conversion including class loader constrains (Foo that is really a Foo doesn't appear to be a Foo) and more. . . So, while 100% in agreement that it is needed, i just don't believe the question of "how should CloudEvent objects represent data" should be answered by |
|
Ok I thought about it and I think i found a solution that reaches the 3 common goals we have:
That's more or less the idea: public interface CloudEvent extends CloudEventAttributes, CloudEventExtensions {
/**
* The event data
*/
@Nullable
<T> T getData();
}This relaxes the assumptions of Now with a method in CloudEvent ev = ....
MyPojo pojo = EventDataCodecProvider.deserializeData(ev, MyPojo.class);This checks the content of If the user needs more complex typing when unmarshalling, he can still use jackson directly pretty easily with: List<AAA> l = mapper.readValue(ev.getData(), new TypeReference<List<AAA>>{})On the write side, protocol bindings implementers needs to invoke something like WDYT about that? |
|
I would strongly advice against using " T". This does not allow knowing the type at runtime and creates a false feeling of being dynamic, although it is not. So, -1 from me (if counts). |
|
As stated before I am also -1 on this one. Returning |
|
So, what are the next steps here? Any proposals on the table? |
|
Yes, this PR has been rejected twice with detailed explanation(s) with no acceptable counter arguments . Personally I am not sure what else to add. Consider closing it and move on. |
|
This is a though one.. Maybe it would be possible to provide an alternative
implementation that implements the CloudEvent interface but also allow a
generic type (or simply Object). Implementations supporting optimizations
like this could instance-of check to see if it's possible to avoid driect
use of the byte-array. The implementation must obviously support
serializing the content to a byte-array as well but this could be done
lazily (and one could use memoization for caching if needed).
fre 7 aug. 2020 kl. 14:32 skrev Oleg Zhurakousky <notifications@github.com>:
Yes, this PR has been rejected twice with detailed explanation(s) with no
acceptable counter arguments . Personally I am not sure what else to add.
Consider closing it and move on.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#198 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AABNVFPAE3AKX65RJAJPZB3R7PX6PANCNFSM4OZJVW7A>
.
--
Sent from my phone
|
|
@johanhaleby what you're saying is very widely used pattern and personally I am +1 for it. Basically having specialized implementations that may provide extra functionality. In this case it doesn't even have to be one. Could be several or even hierarchy, but the core interface as far as I am concerned is final and IMHO I do not foresee any changes to it - ever. |
|
Also, those specializations should live in |
That's what i'm struggling to solve. When you go down the road of allowing a "non byte array", you end up with the requirements of mapping T to byte array. But, in order to fix #196, you need a solution to this problem anyway. We could, as you sad, relax the implementation of The only doable solution I see is implementing only serialization with jackson (from Object to byte[]) and this could also fit straight in the |
|
FTR I second the idea of not having the notion of "typed CloudEvent" in |
I'm not proposing any "typed CloudEvent" here 😄 But yeah that's something that could be done in future in core, like the |
|
IMO the current one is fine (well, I would still prefer |
|
I'm sorry @bsideup , but TBH I don't get why relaxing |
|
@slinkydeveloper because it makes it dynamic. Either make it |
|
In fact, even |
|
Ok I'm trying a second iteration, stay tuned |
|
In occurrent I've run into a similar issue. My current approach is to add a level of indirection and instead of returning a byte array ( |
|
@johanhaleby this is an approach adopted by other sdks too, eg: https://github.com/cloudevents/sdk-rust/blob/master/src/event/data.rs#L5 I think it might be interesting to explore it, I personally like a lot the kind of union types |
|
Second try: #208 |
|
Replaced by #250 |
This PR allows
CloudEventto contain a data field different frombyte[]. The user doesn't need to have a prior knowledge of the type ofdataandEventFormat/protocol bindings doesn't need to know it too. When the user extracts thedatafromCloudEvent, then thedatacan be converted usinggetData(Class). On the other end, theCloudEventmight contain half middle representation data field likeJsonNodefor json.In order to support marshalling/unmarshalling from/to
byte[], I added the concept ofEventDataCodecand implemented it in the jackson module.Signed-off-by: Francesco Guardiani francescoguard@gmail.com