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