/**
* Map the specified URI.
*/
private final void internalMap(CharChunk host, CharChunk uri,
MappingData mappingData)
throws Exception {
uri.setLimit(-1);
Context[] contexts = null;
Context context = null;
int nesting = 0;
// Virtual host mapping
if (mappingData.host == null) {
Host[] hosts = this.hosts;
int pos = findIgnoreCase(hosts, host);
if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
mappingData.host = hosts[pos].object;
contexts = hosts[pos].contextList.contexts;
nesting = hosts[pos].contextList.nesting;
} else {
if (defaultHostName == null) {
return;
}
pos = find(hosts, defaultHostName);
if ((pos != -1) && (defaultHostName.equals(hosts[pos].name))) {
mappingData.host = hosts[pos].object;
contexts = hosts[pos].contextList.contexts;
nesting = hosts[pos].contextList.nesting;
} else {
return;
}
}
}
// Context mapping
if (mappingData.context == null) {
int pos = find(contexts, uri);//L1
if (pos == -1) {
return;
}
int lastSlash = -1;
int uriEnd = uri.getEnd();
int length = -1;
boolean found = false;
while (pos >= 0) {
if (uri.startsWith(contexts[pos].name)) {
length = contexts[pos].name.length();
if (uri.getLength() == length) {
found = true;
break;
} else if (uri.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = nthSlash(uri, nesting + 1);
} else {
lastSlash = lastSlash(uri);
}
uri.setEnd(lastSlash);
pos = find(contexts, uri);
}
uri.setEnd(uriEnd);
if (!found) {//L4
if (contexts[0].name.equals("")) {
context = contexts[0];
}
} else {
context = contexts[pos];
}
if (context != null) {
mappingData.context = context.object;
mappingData.contextPath.setString(context.name);
}
}
// Wrapper mapping
if ((context != null) && (mappingData.wrapper == null)) {
internalMapWrapper(context, uri, mappingData);
}
}
代码位置:org.apache.tomcat.util.http.mapper.Mapper;
先是根据目标host拿到该host下拥有的所有的context(我们比较关心的ServletContext对象则存放在context中),接下来的过程便是根据internalMap参数二uri(本次Http请求的uri,如http://192.168.1.101:8080/test/a.htm,则uri是"/test/a.htm")去匹配到哪个context(context.name为各web项目的根目录名,此时的匹配目标便是context.name);
L1行方法:
/**
* Find a map elemnt given its name in a sorted array of map elements.
* This will return the index for the closest inferior or equal item in the
* given array.
*/
private static final int find(MapElement[] map, CharChunk name) {
return find(map, name, name.getStart(), name.getEnd());//L2
}
其中参数一map为上文L1行的contexts ,参数二name为L2行的uri
L2行代码:
/**
* Find a map elemnt given its name in a sorted array of map elements.
* This will return the index for the closest inferior or equal item in the
* given array.
*/
private static final int find(MapElement[] map, CharChunk name,
int start, int end) {
int a = 0;
int b = map.length - 1;
// Special cases: -1 and 0
if (b == -1) {
return -1;
}
if (compare(name, start, end, map[0].name) < 0 ) {
return -1;
}
if (b == 0) {
return 0;
}
int i = 0;
while (true) {
i = (b + a) / 2;
int result = compare(name, start, end, map[i].name);
if (result == 1) {
a = i;
} else if (result == 0) {
return i;
} else {
b = i;
}
if ((b - a) == 1) {
int result2 = compare(name, start, end, map[b].name);
if (result2 < 0) {
return a;
} else {
return b;
}
}
}
}
可以看到这里用了二分查找法,意味着contexts是已经排序过的了,方法返回的目标context(切确的说是可能是目标的context)在数组中索引号,当没有找到时,在L4行可以看出使用的是默认的context,也就是"/"。