On Mon, Jul 13, 2009 at 5:44 PM, Hadrian Zbarcea<hzbar...@gmail.com> wrote: > Hi, > > Are you in fact proposing to rename get/setOut() to get/setMessage()? And > hasOut() to be renamed as messageChanged(), kinda? This is pretty much how > it sounds to me. > > I like the semantic cleanup. Not sure about passing the message instead of > the exchange. That would have a significant impact on projects using camel > and I don't think the benefit would justify it. Its hardly a significant impact. Removing the generics is a bigger impact that changing a single interface
And what they used to do in the AggregationStrategy (with much confusion, may I stress out - see the user mailinglist) public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { // hmmm is my payload in IN or OUT today? String oldBody = oldExchange.getIn().getBody(String.class); String newBody = newExchange.getIn().getBody(String.class); ... do more stuff ... jesus should I store the aggregated body on IN or OUT .. if I store it in the wrong one I lose all my headers ... . help Camel riders!!! ... for instance as: oldExchange.getOut().setBody(aggregatedBody); return oldExchange; } And this simpler API public Message aggregate(Message oldMessage, Exchange newMessage) { // yeah I have the message directly no more IN or OUT confusion String oldBody = oldMessage.getBody(String.class); String newBody = newMessage.getBody(String.class); ... do more stuff ... great no more lost headers } The API migration is super simple. Before they had to go from exchange -> IN or OUT (???) -> Message Now they have it directly. > > I would love for others to pitch in and express their thoughts. > Hadrian > > > > On Jul 12, 2009, at 5:23 AM, Claus Ibsen wrote: > >> Hi >> >> Starting the kettle to brew me a grok of hot coffee, as its sunday >> morning and I guess I got the energy to toss in my 2$ in the bucket of >> ideas >> Now that we are into the open talks and that it seems we can open the >> box with the IN, OUT, FAULT again. >> >> I advocate for only one message on the implementation side. On the API >> side we could still have some notion of IN / OUT to keep the API >> migration / impact simpler. But at the end having one message API >> makes it simpler. There could be a boolean to test whether an output >> has been set or not. Just as if we have a test for fault. >> >> >> Proposed Exchange API >> ================== >> >> Message getMessage() >> void setMessage(Message msg); >> >> boolean messageChanged(); >> -- false = message have newer been mutated, eg its still the same input >> -- true = the message or message body have been mutated in some way >> (setMessage or setBody called) >> -- need to find a good name for this method, eg testing whether the >> message have been changed/updated or not >> >> That leaves us with a simple API. >> >> Then we do not need to worry about all the message management >> internally in Camel, where we need to figure out whether to set data >> on IN or OUT and what else we kinda do, and it gets a bit complex over >> time how to do this correctly. >> >> >> MEP: InOnly >> ========= >> Now we can do as Hadrian want. Newer return something in OUT. And >> leave the original input on the Exchange. >> Here we need to use James CopyOnWrite technique so we can preserve the >> original message in case its mutated during routing. >> >> >> MEP: InOut >> ========= >> If an OUT message have been set (eg there is a reply), can be >> determined if the messageChanged() == true. (then its not the original >> message anymore). And yes the current code does this. It will just >> copy whatever there is in IN and use it as OUT if no OUT was set. >> With this proposal this will be improved as its easier to determine if >> there is an OUT message or not. >> >> >> Original message >> ============= >> Currently there is an API to get the original message from the >> UnitOfWork as its needed when doing redeliveries and a message was >> doomed >> and had to be moved to the dead letter channel. Then it makes much >> more sense to move the original message than the current mutated >> message. >> ( i wonder if it should be default behavior ) >> >> >> AggregationStrategy >> =============== >> Another benefit with a single getMessage() is that end users using >> AggregationStrategy will not be confused how to use it. >> Should I look in IN or OUT. And the current code can actually be a >> "lucky draw" as whether the data is in IN or OUT depending on facts >> such as how the route path is. >> We can even change it to pass in Message object instead of bare bone >> Exchange. You can always go from Message to exchange using >> getExchange(). >> >> >> All the processors / components / data formats >> =================================== >> Logic will be easier as well as they do not need to cater for IN / OUT >> and where and how to set a result. Just work on the Message. >> >> >> Use @deprecated for steady migration >> ============================ >> Hadrian suggested that for the API migration to a single getMessage >> you could let the getIn/setIn getOut/setOut delegate to >> getMessage/setMessage. >> And then mark them as @deprecated and then gradually change the camel >> code as we goes. So I do not think the hold up / change would take a >> lot of time and energy to get done. >> >> >> Final words >> ======== >> So if it was possible to simplify the API and reduce the IN OUT FAULT >> to a simpler API, even go as far as I would like with a single >> Message. >> Then that would be really great and worth a hold up for a imminent 2.0 >> release. >> >> >> Other frameworks >> ============= >> I guess it wasn't final words after all :) Just wanted to say that >> Mule, Spring Integration also just have a single message for the >> message. >> And yes I think their projects are also successful so it is not a loss >> that they do not have IN OUT. In fact I think their API is easier to >> work with than Camel. >> >> We are fortunate that most people with Camel do not work directly with >> Exchange but work more with Camel returning the message as an expected >> body type using its type converters. That helps a lot. >> >> But we have stories form Camel 1.x where people get confused whey >> Camel return their original input in some situations where it was not >> expected in the OUT message. Or the fact producer template sendBody is >> not void in 1.x. Then people think it can be used for InOut. >> >> Okay end of mail. >> >> >> On Fri, Jul 10, 2009 at 5:16 PM, James Strachan<james.strac...@gmail.com> >> wrote: >>> >>> 2009/7/10 Hadrian Zbarcea <hzbar...@gmail.com>: >>>> >>>> Moved this slightly different topic to a separate thread. >>>> >>>> ++1 from me. >>>> >>>> Do we need a vote on this one or it's a consensus? Claus, it looks like >>>> you >>>> agree, but pointed out that there is some work involved, correct? >>>> >>>> Hadrian >>>> >>>> >>>> >>>> On Jul 10, 2009, at 8:40 AM, James Strachan wrote: >>>> >>>>> 2009/7/10 Claus Ibsen <claus.ib...@gmail.com>: >>>>>> >>>>>> On Fri, Jul 10, 2009 at 1:20 PM, James >>>>>> Strachan<james.strac...@gmail.com> >>>>>> wrote: >>>>>>> >>>>>>> 2009/7/10 Claus Ibsen <claus.ib...@gmail.com>: >>>>>>>> >>>>>>>> Hi >>>>>>>> >>>>>>>> Another update on the IN vs OUT when you send in an Exchange. >>>>>>>> >>>>>>>> The ProducerCache that is doing the actual sending when using >>>>>>>> template >>>>>>>> or sendTo etc, its basically doing all send X to endpoint. >>>>>>>> >>>>>>>> Well I am playing with to let it adhere to the principle Hadrian >>>>>>>> pointed out. He wanted the IN to be more static. >>>>>>>> And we cannot get there yet when you do routing as all the >>>>>>>> processors >>>>>>>> rely on IN being able to mutate during routing. >>>>>>>> >>>>>>>> Anyway my grief is that when you send in an Exchange the result >>>>>>>> would >>>>>>>> sometimes be stored on IN and not OUT. >>>>>>>> What I want it to do always is to store the result in OUT and keep >>>>>>>> IN >>>>>>>> as the original input. >>>>>>>> >>>>>>>> The code to do this is now a bit more complex than just before >>>>>>>> >>>>>>>> // copy the original input >>>>>>>> Message original = exchange.getIn().copy(); >>>>>>>> >>>>>>>> producer.process(exchange); >>>>>>>> >>>>>>>> // if no OUT then set current IN as result (except for >>>>>>>> optional out) >>>>>>>> if (!exchange.hasOut() && exchange.getPattern() != >>>>>>>> ExchangePattern.InOptionalOut) { >>>>>>>> // but only if its not the same as original IN to >>>>>>>> avoid duplicating it >>>>>>>> // and to adhere to the fact that there was no OUT >>>>>>>> result at all >>>>>>>> if (original.getBody() != null && >>>>>>>> !original.getBody().equals(exchange.getIn().getBody())) { >>>>>>>> exchange.setOut(exchange.getIn()); >>>>>>>> } >>>>>>>> } >>>>>>>> // and restore original in >>>>>>>> exchange.setIn(original); >>>>>>>> >>>>>>>> return exchange; >>>>>>>> >>>>>>>> >>>>>>>> What I need to do is to copy the original IN message as it can be >>>>>>>> mutated during routing. >>>>>>> >>>>>>> How about we prevent mutation of the IN message? Create a Message >>>>>>> facade which throws UnsupportedOperationException if you try to >>>>>>> mutate >>>>>>> it in any way. Then we can pass the same read-only Message around as >>>>>>> the IN within retry loops or from step to step if no new output is >>>>>>> created (e.g. in a content based router where you just move a Message >>>>>>> to the right endpoint without changing it) >>>>>>> >>>>>> >>>>>> A good idea but will break a lot of logic in Camel. >>>>> >>>>> Agreed. But with the benefit that we'd be able to get rid of all the >>>>> defensive copies in our code; plus we'd be able to pass the same >>>>> Message from step to step. The API would be a bit more clean; to >>>>> change the output, you create an OUT message (maybe by copying the >>>>> IN). >>>>> >>>>> >>>>>> Most of the Camel >>>>>> processors work on the IN message and set the result on either IN or >>>>>> OUT. At best they set it on OUT. But then the IN is always the >>>>>> original input? Or am I mistaking? >>>>> >>>>> Yeah, we'd have to patch code to no longer mutate IN >>>>> >>>>> >>>>>> How will this work with the Pipes And Filters EIP if the IN is >>>>>> immutable and always the original input? >>>>> >>>>> If no OUT, then no output was created, so pass the IN along... >>>>> >>>>> >>>>> OK how about this; a CopyOnWriteMessageFacade which does not mutate >>>>> the original message at all ever; if a message is used in a purely >>>>> read only way, it does nothing but delegate to the original message - >>>>> but then as soon as someone mutates it, it creates a copy and uses >>>>> that from that point on? >>>>> >>>>> i.e. make the copy of the message lazy - and only make a copy when a >>>>> Processor really does try to mutate the Message? >>>>> >>>>> Then we'd get the best of both worlds; avoid breaking old code but >>>>> avoid tons of unnecessary copies? >>> >>> BTW then using the current API you could have a Message and then call >>> >>> Message origin = ...; >>> Message newMsg = origin.copy().copy().copy().copy(); >>> >>> and the message would not actually be copied at all; new would just be >>> a CopyOnWriteMessageFacade which would hold a reference to 'origin' as >>> the readOnlyMessage (which it never mutates). >>> >>> The copy would only take place if you did >>> >>> newMsg.setBody("foo") >>> >>> -- >>> James >>> ------- >>> http://macstrac.blogspot.com/ >>> >>> Open Source Integration >>> http://fusesource.com/ >>> >> >> >> >> -- >> Claus Ibsen >> Apache Camel Committer >> >> Open Source Integration: http://fusesource.com >> Blog: http://davsclaus.blogspot.com/ >> Twitter: http://twitter.com/davsclaus > > -- Claus Ibsen Apache Camel Committer Open Source Integration: http://fusesource.com Blog: http://davsclaus.blogspot.com/ Twitter: http://twitter.com/davsclaus