Hi

I have just attached a much better implementation of the map, this
time all code is in a single class and it does not use holder objects
such as a KeyValuePair when storing the value.

It also works out of the box with scripting engines that can access
and manipulate the headers such as Freemarker and Velocity.

The changes needed in DefaultMessage is basically to use this map
instead of a regular HashMap.
And when its copied to another map the original keys is automatic retained.



On Sat, Aug 8, 2009 at 12:42 PM, Claus Ibsen<claus.ib...@gmail.com> wrote:
> Hi
>
> For background see ticket CAMEL-1886
> https://issues.apache.org/activemq/browse/CAMEL-1886
>
> The issue is that headers is stored using keys that are case
> sensitive, and that leads to a few problems and differences in Camel:
>
> - lookup is cumbersome as you must match case 100%
> - errors in camel components that is unable to realize a header
> already exists however with a different (case)
> - some protocols are agnostic to header cases, such as HTTP (and all
> its different transports using HTTP), Mail, and whatnot?
> - camel-mail stores notoriously headers in lower keys only
> - etc.
>
> And most importantly
> - no one wants to send different values using a key that have
> different case, such as:
>  Foo=cheese
>  FOO=cake
>  foo=beer
>
> So James and I had a chat about it and to see if would could do
> something about it.
>
> The real reason that I picked up this issue is the tuning experiment
> with Camel 2.x
> http://camel.apache.org/camel-2x-speed-optimizations.html
>
> Where we want to take a few short cuts and offer header lookup on the
> source directly to avoid the "tiny" overhead of extracting headers
> from the source
> and populate the camel message. This is useful if you do message
> routing that only uses headers. The camel-jms component did already
> offer this but
> it also had some inconsistency in this relation.
>
>
> I have attempted a resolution in the tuning branch but discovered an
> issue, that it does take some more grunt work to keep the lower case
> index in sync etc.
> So I wanted to try a different approach
>
> CaseInsenstiveMap
> ===============
>
> I have implemented a small case insenstive map so we can store headers
> in 1 map instead of keeping 2 maps in sync.
> I am still amazed why such a Map does not exists directly in the JDK itself.
>
> What does this offer then
> - lookup is easy as you can lookup using any case
>  and in the future we could also offer lookup for special keys people
> spell differently such as: ContentType, content-type, content-type,
> content_type and whatnot.
> - put will also ensure that it replaces existing values
> - and most importantly that it offers this as well when end users work
> directly on the map using:
>     exchange.getIn().getHeaders().put("foo", "beer")
>  That will be able to replace existing keys if they are stored using:
> Foo, FOO or what not
>  This is not possible today and or with a 2nd map that tracks lower
> case indexes
>
> So how to retain the original keys.
>
> Well I implemented that as well, so when you e.g. copy the map to
> another or extract from it using keySet it exposes a Set that uses the
> original key cases.
> So if you have stored using
>   exchange.getIn().setHeader("Foo", "cheese")
>
> Then the key in the ketSet is the original key = Foo.
> But you can do lookup using: foo, FOO, FoO, FOo, and whatnot :)
>
> Using this implementation I actually find a few bugs in existing unit
> tests and camel components that double add headers using different
> cases.
> For example camel-cxf in the new RS support.
>
>
> I will later attach the code to the CAMEL-1886 ticket for people of
> the community to take a look and review.
>
>
> Here is a snippet from one of the test methods to give an idea
>
>    public void testLookupCaseAgnosticAddHeaderRemoveHeader() {
>        Map<String, Object> map = new CaseInsensitiveMap();
>        assertNull(map.get("foo"));
>
>        map.put("foo", "cheese");
>
>        assertEquals("cheese", map.get("foo"));
>        assertEquals("cheese", map.get("Foo"));
>        assertEquals("cheese", map.get("FOO"));
>        assertNull(map.get("unknown"));
>
>        map.put("bar", "beer");
>
>        assertEquals("beer", map.get("bar"));
>        assertEquals("beer", map.get("Bar"));
>        assertEquals("beer", map.get("BAR"));
>        assertNull(map.get("unknown"));
>
>        map.remove("bar");
>        assertNull(map.get("bar"));
>        assertNull(map.get("unknown"));
>    }
>
>
> And when using the Message API
>
>    public void testRemoveWithDifferentCase() {
>        Message msg = new DefaultMessage();
>        assertNull(msg.getHeader("foo"));
>
>        msg.setHeader("foo", "cheese");
>        msg.setHeader("Foo", "bar");
>
>        assertEquals("bar", msg.getHeader("FOO"));
>        assertEquals("bar", msg.getHeader("foo"));
>        assertEquals("bar", msg.getHeader("Foo"));
>
>        msg.removeHeader("FOO");
>
>        assertEquals(null, msg.getHeader("foo"));
>        assertEquals(null, msg.getHeader("Foo"));
>        assertEquals(null, msg.getHeader("FOO"));
>
>        assertTrue(msg.getHeaders().isEmpty());
>    }
>
>
>
>
>
> --
> 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

Reply via email to