blob: 6b2075b96ea8475ae57bba0185beed0026348ed5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.lite.http;
import java.io.IOException;
import java.util.logging.Logger;
import org.apache.tomcat.lite.http.HttpChannel.HttpService;
import org.apache.tomcat.lite.io.CBuffer;
import org.apache.tomcat.lite.io.FileConnector;
import org.apache.tomcat.lite.io.BBucket;
/**
* Mapper, which implements the servlet API mapping rules (which are derived
* from the HTTP rules).
*
* This class doesn't use JNDI.
*/
public class BaseMapper {
private static Logger logger =
Logger.getLogger(BaseMapper.class.getName());
// TODO:
/**
* Mapping should be done on bytes - as received from net, before
* translation to chars. This would allow setting the default charset
* for the context - or even executing the servlet and letting it specify
* the charset to use for further decoding.
*
*/
public static interface Mapper {
public void map(BBucket host, BBucket url, MappingData md);
}
/**
* Like BaseMapper, for a Context.
*/
public static class ServiceMapper extends BaseMapper {
/**
* Context associated with this wrapper, used for wrapper mapping.
*/
public BaseMapper.Context contextMapElement = new BaseMapper.Context(this);
/**
* Set context, used for wrapper mapping (request dispatcher).
*
* @param welcomeResources Welcome files defined for this context
*/
public void setContext(String path, String[] welcomeResources) {
contextMapElement.name = path;
contextMapElement.welcomeResources = welcomeResources;
}
/**
* Add a wrapper to the context associated with this wrapper.
*
* @param path Wrapper mapping
* @param wrapper The Wrapper object
*/
public void addWrapper(String path, Object wrapper) {
addWrapper(contextMapElement, path, wrapper);
}
public void addWrapper(String path, Object wrapper, boolean jspWildCard) {
addWrapper(contextMapElement, path, wrapper, jspWildCard);
}
/**
* Remove a wrapper from the context associated with this wrapper.
*
* @param path Wrapper mapping
*/
public void removeWrapper(String path) {
removeWrapper(contextMapElement, path);
}
// /**
// * Map the specified URI relative to the context,
// * mutating the given mapping data.
// *
// * @param uri URI
// * @param mappingData This structure will contain the result of the mapping
// * operation
// */
// public void map(CBuffer uri, MappingData mappingData)
// throws Exception {
//
// CBuffer uricc = uri.getCharBuffer();
// internalMapWrapper(contextMapElement, uricc, mappingData);
//
// }
}
/**
* Array containing the virtual hosts definitions.
*/
Host[] hosts = new Host[0];
/**
* If no other host is found.
* For single-host servers ( most common ) this is the only one
* used.
*/
Host defaultHost = new Host();
public BaseMapper() {
defaultHost.contextList = new ContextList();
}
// --------------------------------------------------------- Public Methods
public synchronized Host addHost(String name) {
if (name == null) {
name = "localhost";
}
Host[] newHosts = new Host[hosts.length + 1];
Host newHost = new Host();
newHost.name = name;
newHost.contextList = new ContextList();
if (insertMap(hosts, newHosts, newHost)) {
hosts = newHosts;
}
return newHost;
}
/**
* Remove a host from the mapper.
*
* @param name Virtual host name
*/
public synchronized void removeHost(String name) {
// Find and remove the old host
int pos = find(hosts, name);
if (pos < 0) {
return;
}
Object host = hosts[pos].object;
Host[] newHosts = new Host[hosts.length - 1];
if (removeMap(hosts, newHosts, name)) {
hosts = newHosts;
}
// Remove all aliases (they will map to the same host object)
for (int i = 0; i < newHosts.length; i++) {
if (newHosts[i].object == host) {
Host[] newHosts2 = new Host[hosts.length - 1];
if (removeMap(hosts, newHosts2, newHosts[i].name)) {
hosts = newHosts2;
}
}
}
}
/**
* Add an alias to an existing host.
* @param name The name of the host
* @param alias The alias to add
*/
public synchronized void addHostAlias(String name, String alias) {
int pos = find(hosts, name);
if (pos < 0) {
// Should not be adding an alias for a host that doesn't exist but
// just in case...
return;
}
Host realHost = hosts[pos];
Host[] newHosts = new Host[hosts.length + 1];
Host newHost = new Host();
newHost.name = alias;
newHost.contextList = realHost.contextList;
newHost.object = realHost;
if (insertMap(hosts, newHosts, newHost)) {
hosts = newHosts;
}
}
private Host getHost(String host) {
return getHost(CBuffer.newInstance().append(host));
}
private Host getHost(CBuffer host) {
if (hosts == null || hosts.length <= 1 || host == null
|| host.length() == 0 || host.equals("")) {
return defaultHost;
} else {
Host[] hosts = this.hosts;
// TODO: if hosts.length == 1 or defaultHost ?
int pos = findIgnoreCase(hosts, host);
if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
return hosts[pos];
} else {
return defaultHost;
}
}
}
private Host getOrCreateHost(String hostName) {
Host host = getHost(CBuffer.newInstance().append(hostName));
if (host == null) {
host = addHost(hostName);
}
return host;
}
// Contexts
/**
* Add a new Context to an existing Host.
*
* @param hostName Virtual host name this context belongs to
* @param path Context path
* @param context Context object
* @param welcomeResources Welcome files defined for this context
* @param resources Static resources of the context
* @param ctxService
*/
public BaseMapper.Context addContext(String hostName, String path, Object context,
String[] welcomeResources, FileConnector resources,
HttpChannel.HttpService ctxService) {
if (path == null) {
path = "/";
}
Host host = getOrCreateHost(hostName);
int slashCount = slashCount(path);
synchronized (host) {
BaseMapper.Context[] contexts = host.contextList.contexts;
// Update nesting
if (slashCount > host.contextList.nesting) {
host.contextList.nesting = slashCount;
}
for (int i = 0; i < contexts.length; i++) {
if (path.equals(contexts[i].name)) {
return contexts[i];
}
}
BaseMapper.Context[] newContexts = new BaseMapper.Context[contexts.length + 1];
BaseMapper.Context newContext = new BaseMapper.Context(this);
newContext.name = path;
newContext.object = context;
if (welcomeResources != null) {
newContext.welcomeResources = welcomeResources;
}
newContext.resources = resources;
if (ctxService != null) {
newContext.defaultWrapper = new BaseMapper.ServiceMapping();
newContext.defaultWrapper.object = ctxService;
}
if (insertMap(contexts, newContexts, newContext)) {
host.contextList.contexts = newContexts;
}
return newContext;
}
}
/**
* Remove a context from an existing host.
*
* @param hostName Virtual host name this context belongs to
* @param path Context path
*/
public void removeContext(String hostName, String path) {
Host host = getHost(hostName);
synchronized (host) {
BaseMapper.Context[] contexts = host.contextList.contexts;
if( contexts.length == 0 ){
return;
}
BaseMapper.Context[] newContexts = new BaseMapper.Context[contexts.length - 1];
if (removeMap(contexts, newContexts, path)) {
host.contextList.contexts = newContexts;
// Recalculate nesting
host.contextList.nesting = 0;
for (int i = 0; i < newContexts.length; i++) {
int slashCount = slashCount(newContexts[i].name);
if (slashCount > host.contextList.nesting) {
host.contextList.nesting = slashCount;
}
}
}
}
}
/**
* Add a new Wrapper to an existing Context.
*
* @param hostName Virtual host name this wrapper belongs to
* @param contextPath Context path this wrapper belongs to
* @param path Wrapper mapping
* @param wrapper Wrapper object
*/
public void addWrapper(String hostName, String contextPath, String path,
Object wrapper) {
addWrapper(hostName, contextPath, path, wrapper, false);
}
public void addWrapper(String hostName, String contextPath, String path,
Object wrapper, boolean jspWildCard) {
Host host = getHost(hostName);
BaseMapper.Context[] contexts = host.contextList.contexts;
int pos2 = find(contexts, contextPath);
if( pos2<0 ) {
logger.severe("No context found: " + contextPath );
return;
}
BaseMapper.Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
addWrapper(context, path, wrapper, jspWildCard);
}
}
public void addWrapper(BaseMapper.Context context, String path, Object wrapper) {
addWrapper(context, path, wrapper, false);
}
/**
* Adds a wrapper to the given context.
*
* @param context The context to which to add the wrapper
* @param path Wrapper mapping
* @param wrapper The Wrapper object
* @param jspWildCard true if the wrapper corresponds to the JspServlet
* and the mapping path contains a wildcard; false otherwise
*/
protected void addWrapper(BaseMapper.Context context, String path, Object wrapper,
boolean jspWildCard) {
synchronized (context) {
BaseMapper.ServiceMapping newWrapper = new BaseMapper.ServiceMapping();
newWrapper.object = wrapper;
newWrapper.jspWildCard = jspWildCard;
if (path.endsWith("/*")) {
// Wildcard wrapper
newWrapper.name = path.substring(0, path.length() - 2);
BaseMapper.ServiceMapping[] oldWrappers = context.wildcardWrappers;
BaseMapper.ServiceMapping[] newWrappers =
new BaseMapper.ServiceMapping[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.wildcardWrappers = newWrappers;
int slashCount = slashCount(newWrapper.name);
if (slashCount > context.nesting) {
context.nesting = slashCount;
}
}
} else if (path.startsWith("*.")) {
// Extension wrapper
newWrapper.name = path.substring(2);
BaseMapper.ServiceMapping[] oldWrappers = context.extensionWrappers;
BaseMapper.ServiceMapping[] newWrappers =
new BaseMapper.ServiceMapping[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.extensionWrappers = newWrappers;
}
} else if (path.equals("/")) {
// Default wrapper
newWrapper.name = "";
context.defaultWrapper = newWrapper;
} else {
// Exact wrapper
newWrapper.name = path;
BaseMapper.ServiceMapping[] oldWrappers = context.exactWrappers;
BaseMapper.ServiceMapping[] newWrappers =
new BaseMapper.ServiceMapping[oldWrappers.length + 1];
if (insertMap(oldWrappers, newWrappers, newWrapper)) {
context.exactWrappers = newWrappers;
}
}
}
}
/**
* Remove a wrapper from an existing context.
*
* @param hostName Virtual host name this wrapper belongs to
* @param contextPath Context path this wrapper belongs to
* @param path Wrapper mapping
*/
public void removeWrapper(String hostName, String contextPath,
String path) {
Host host = getHost(hostName);
BaseMapper.Context[] contexts = host.contextList.contexts;
int pos2 = find(contexts, contextPath);
if (pos2 < 0) {
return;
}
BaseMapper.Context context = contexts[pos2];
if (context.name.equals(contextPath)) {
removeWrapper(context, path);
}
}
protected void removeWrapper(BaseMapper.Context context, String path) {
synchronized (context) {
if (path.endsWith("/*")) {
// Wildcard wrapper
String name = path.substring(0, path.length() - 2);
BaseMapper.ServiceMapping[] oldWrappers = context.wildcardWrappers;
BaseMapper.ServiceMapping[] newWrappers =
new BaseMapper.ServiceMapping[oldWrappers.length - 1];
if (removeMap(oldWrappers, newWrappers, name)) {
// Recalculate nesting
context.nesting = 0;
for (int i = 0; i < newWrappers.length; i++) {
int slashCount = slashCount(newWrappers[i].name);
if (slashCount > context.nesting) {
context.nesting = slashCount;
}
}
context.wildcardWrappers = newWrappers;
}
} else if (path.startsWith("*.")) {
// Extension wrapper
String name = path.substring(2);
BaseMapper.ServiceMapping[] oldWrappers = context.extensionWrappers;
BaseMapper.ServiceMapping[] newWrappers =
new BaseMapper.ServiceMapping[oldWrappers.length - 1];
if (removeMap(oldWrappers, newWrappers, name)) {
context.extensionWrappers = newWrappers;
}
} else if (path.equals("/")) {
// Default wrapper
context.defaultWrapper = null;
} else {
// Exact wrapper
String name = path;
BaseMapper.ServiceMapping[] oldWrappers = context.exactWrappers;
BaseMapper.ServiceMapping[] newWrappers =
new BaseMapper.ServiceMapping[oldWrappers.length - 1];
if (removeMap(oldWrappers, newWrappers, name)) {
context.exactWrappers = newWrappers;
}
}
}
}
/**
* Map the specified host name and URI, mutating the given mapping data.
*
* @param host Virtual host name
* @param uri URI
* @param mappingData This structure will contain the result of the mapping
* operation
*/
public void map(CBuffer host, CBuffer uri,
MappingData mappingData)
throws Exception {
internalMap(host.length() == 0 ? null :
host, uri, mappingData);
}
// -------------------------------------------------------- Private Methods
// public Context mapContext(CBuffer host, CBuffer url);
/**
* Map the specified URI.
*/
private final void internalMap(CBuffer host, CBuffer uri,
MappingData mappingData)
throws Exception {
BaseMapper.Context[] contexts = null;
BaseMapper.Context context = null;
int nesting = 0;
// Virtual host mapping
Host mappedHost = getHost(host);
contexts = mappedHost.contextList.contexts;
nesting = mappedHost.contextList.nesting;
// Context mapping
if (contexts.length == 0) {
return;
}
if (mappingData.context == null) {
if (nesting < 1 || contexts.length == 1 && "".equals(contexts[0].name)) {
// if 1 context (default) -> fast return
context = contexts[0];
} else if (nesting == 1) {
// if all contexts are 1-component-only
int nextSlash = uri.indexOf('/', 1);
if (nextSlash == -1) {
nextSlash = uri.length();
}
mappingData.contextPath.set(uri, 0, nextSlash);
int pos = find(contexts, uri);
if (pos == -1) {
pos = find(contexts, "/");
}
if (pos >= 0) {
context = contexts[pos];
}
} else {
int pos = find(contexts, uri);
if (pos >= 0) {
int lastSlash = -1;
int length = -1;
boolean found = false;
CBuffer tmp = mappingData.tmpPrefix;
tmp.wrap(uri, 0, uri.length());
while (pos >= 0) {
if (tmp.startsWith(contexts[pos].name)) {
length = contexts[pos].name.length();
if (tmp.length() == length) {
found = true;
break;
} else if (tmp.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = tmp.nthSlash(nesting + 1);
} else {
lastSlash = tmp.lastIndexOf('/');
}
tmp.delete(lastSlash);
pos = find(contexts, tmp);
}
if (!found) {
if (contexts[0].name.equals("")) {
context = contexts[0];
}
} else {
context = contexts[pos];
}
}
}
if (context != null) {
mappingData.context = context.object;
mappingData.contextPath.set(context.name);
}
}
// Wrapper mapping
if ((context != null) && (mappingData.getServiceObject() == null)) {
internalMapWrapper(context, uri, mappingData);
}
}
/**
* Wrapper mapping, using servlet rules.
*/
protected final void internalMapWrapper(
BaseMapper.Context context,
CBuffer url,
MappingData mappingData)
throws Exception {
boolean noServletPath = false;
if (url.length() < context.name.length()) {
throw new IOException("Invalid mapping " + context.name + " " +
url);
}
try {
// Set the servlet path.
mappingData.tmpServletPath.set(url,
context.name.length(),
url.length() - context.name.length());
if (mappingData.tmpServletPath.length() == 0) {
mappingData.tmpServletPath.append('/');
// This is just the context /example or /
if (!context.name.equals("/")) {
noServletPath = true;
}
}
mapAfterContext(context, url, mappingData.tmpServletPath, mappingData,
noServletPath);
} catch (ArrayIndexOutOfBoundsException ex) {
System.err.println(1);
}
}
void mapAfterContext(BaseMapper.Context context,
CBuffer url, CBuffer urlNoContext,
MappingData mappingData, boolean noServletPath)
throws Exception {
// Rule 1 -- Exact Match
BaseMapper.ServiceMapping[] exactWrappers = context.exactWrappers;
internalMapExactWrapper(exactWrappers, urlNoContext, mappingData);
// Rule 2 -- Prefix Match
boolean checkJspWelcomeFiles = false;
BaseMapper.ServiceMapping[] wildcardWrappers = context.wildcardWrappers;
if (mappingData.getServiceObject() == null) {
internalMapWildcardWrapper(wildcardWrappers, context.nesting,
urlNoContext, mappingData);
if (mappingData.getServiceObject() != null
&& mappingData.service.jspWildCard) {
if (urlNoContext.lastChar() == '/') {
/*
* Path ending in '/' was mapped to JSP servlet based on
* wildcard match (e.g., as specified in url-pattern of a
* jsp-property-group.
* Force the context's welcome files, which are interpreted
* as JSP files (since they match the url-pattern), to be
* considered. See Bugzilla 27664.
*/
mappingData.service = null;
checkJspWelcomeFiles = true;
} else {
// See Bugzilla 27704
mappingData.wrapperPath.set(urlNoContext);
mappingData.pathInfo.recycle();
}
}
}
if(mappingData.getServiceObject() == null && noServletPath) {
// The path is empty, redirect to "/"
mappingData.redirectPath.set(context.name);
mappingData.redirectPath.append("/");
return;
}
// Rule 3 -- Extension Match
BaseMapper.ServiceMapping[] extensionWrappers = context.extensionWrappers;
if (mappingData.getServiceObject() == null && !checkJspWelcomeFiles) {
internalMapExtensionWrapper(extensionWrappers, urlNoContext, mappingData);
}
// Rule 4 -- Welcome resources processing for servlets
if (mappingData.getServiceObject() == null) {
boolean checkWelcomeFiles = checkJspWelcomeFiles;
if (!checkWelcomeFiles) {
checkWelcomeFiles = (urlNoContext.lastChar() == '/');
}
if (checkWelcomeFiles) {
for (int i = 0; (i < context.welcomeResources.length)
&& (mappingData.getServiceObject() == null); i++) {
CBuffer wpath = mappingData.tmpWelcome;
wpath.set(urlNoContext);
wpath.append(context.welcomeResources[i]);
// Rule 4a -- Welcome resources processing for exact macth
internalMapExactWrapper(exactWrappers, urlNoContext, mappingData);
// Rule 4b -- Welcome resources processing for prefix match
if (mappingData.getServiceObject() == null) {
internalMapWildcardWrapper
(wildcardWrappers, context.nesting,
urlNoContext, mappingData);
}
// Rule 4c -- Welcome resources processing
// for physical folder
if (mappingData.getServiceObject() == null
&& context.resources != null) {
String pathStr = urlNoContext.toString();
mapWelcomResource(context, urlNoContext, mappingData,
extensionWrappers, pathStr);
}
}
}
}
// Rule 7 -- Default servlet
if (mappingData.getServiceObject() == null && !checkJspWelcomeFiles) {
if (context.defaultWrapper != null) {
mappingData.service = context.defaultWrapper;
mappingData.requestPath.set(urlNoContext);
mappingData.wrapperPath.set(urlNoContext);
}
// Redirection to a folder
if (context.resources != null && urlNoContext.lastChar() != '/') {
String pathStr = urlNoContext.toString();
mapDefaultServlet(context, urlNoContext, mappingData,
url,
pathStr);
}
}
}
/**
* Filesystem-dependent method:
* if pathStr corresponds to a directory, we'll need to redirect with /
* at end.
*/
protected void mapDefaultServlet(BaseMapper.Context context,
CBuffer path,
MappingData mappingData,
CBuffer url,
String pathStr) throws IOException {
if (context.resources != null
&& context.resources.isDirectory(pathStr)) {
mappingData.redirectPath.set(url);
mappingData.redirectPath.append("/");
} else {
mappingData.requestPath.set(pathStr);
mappingData.wrapperPath.set(pathStr);
}
}
/**
* Filesystem dependent method:
* check if a resource exists in filesystem.
*/
protected void mapWelcomResource(BaseMapper.Context context, CBuffer path,
MappingData mappingData,
BaseMapper.ServiceMapping[] extensionWrappers, String pathStr) {
if (context.resources != null &&
context.resources.isFile(pathStr)) {
internalMapExtensionWrapper(extensionWrappers,
path, mappingData);
if (mappingData.getServiceObject() == null
&& context.defaultWrapper != null) {
mappingData.service = context.defaultWrapper;
mappingData.requestPath.set(path);
mappingData.wrapperPath.set(path);
mappingData.requestPath.set(pathStr);
mappingData.wrapperPath.set(pathStr);
}
}
}
/**
* Exact mapping.
*/
private final void internalMapExactWrapper
(BaseMapper.ServiceMapping[] wrappers, CBuffer path, MappingData mappingData) {
int pos = find(wrappers, path);
if ((pos != -1) && (path.equals(wrappers[pos].name))) {
mappingData.requestPath.set(wrappers[pos].name);
mappingData.wrapperPath.set(wrappers[pos].name);
mappingData.service = wrappers[pos];
}
}
/**
* Prefix mapping. ( /foo/* )
*/
private final void internalMapWildcardWrapper
(BaseMapper.ServiceMapping[] wrappers, int nesting, CBuffer path,
MappingData mappingData) {
int lastSlash = -1;
int length = -1;
CBuffer tmp = mappingData.tmpPrefix;
tmp.wrap(path, 0, path.length());
int pos = find(wrappers, tmp);
if (pos != -1) {
boolean found = false;
while (pos >= 0) {
if (tmp.startsWith(wrappers[pos].name)) {
length = wrappers[pos].name.length();
if (tmp.length() == length) {
found = true;
break;
} else if (tmp.startsWithIgnoreCase("/", length)) {
found = true;
break;
}
}
if (lastSlash == -1) {
lastSlash = tmp.nthSlash(nesting + 1);
} else {
lastSlash = tmp.lastIndexOf('/');
}
tmp.delete(lastSlash);
pos = find(wrappers, tmp);
}
if (found) {
mappingData.wrapperPath.set(wrappers[pos].name);
if (path.length() > length) {
mappingData.pathInfo.set
(path, length, path.length() - length);
}
mappingData.requestPath.set(path);
mappingData.service = wrappers[pos];
}
}
}
/**
* Extension mappings.
*/
protected final void internalMapExtensionWrapper
(BaseMapper.ServiceMapping[] wrappers, CBuffer path, MappingData mappingData) {
int dot = path.getExtension(mappingData.ext, '/', '.');
if (dot >= 0) {
int pos = find(wrappers, mappingData.ext);
if ((pos != -1)
&& (mappingData.ext.equals(wrappers[pos].name))) {
mappingData.wrapperPath.set(path);
mappingData.requestPath.set(path);
mappingData.service = wrappers[pos];
}
}
}
/**
* 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(BaseMapper.Mapping[] map, CBuffer name) {
int a = 0;
int b = map.length - 1;
// Special cases: -1 and 0
if (b == -1) {
return -1;
}
if (name.compare(map[0].name) < 0 ) {
return -1;
}
if (b == 0) {
return 0;
}
int i = 0;
while (true) {
i = (b + a) / 2;
int result = name.compare(map[i].name);
if (result == 1) {
a = i;
} else if (result == 0) {
return i;
} else {
b = i;
}
if ((b - a) == 1) {
int result2 = name.compare(map[b].name);
if (result2 < 0) {
return a;
} else {
return b;
}
}
}
}
/**
* 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 findIgnoreCase(BaseMapper.Mapping[] map,
CBuffer name) {
int a = 0;
int b = map.length - 1;
// Special cases: -1 and 0
if (b == -1) {
return -1;
}
if (name.compareIgnoreCase(map[0].name) < 0 ) {
return -1;
}
if (b == 0) {
return 0;
}
int i = 0;
while (true) {
i = (b + a) / 2;
int result = name.compareIgnoreCase(map[i].name);
if (result == 1) {
a = i;
} else if (result == 0) {
return i;
} else {
b = i;
}
if ((b - a) == 1) {
int result2 = name.compareIgnoreCase(map[b].name);
if (result2 < 0) {
return a;
} else {
return b;
}
}
}
}
/**
* Find a map element 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(BaseMapper.Mapping[] map, String name) {
int a = 0;
int b = map.length - 1;
// Special cases: -1 and 0
if (b == -1) {
return -1;
}
if (name.compareTo(map[0].name) < 0) {
return -1;
}
if (b == 0) {
return 0;
}
int i = 0;
while (true) {
i = (b + a) / 2;
int result = name.compareTo(map[i].name);
if (result > 0) {
a = i;
} else if (result == 0) {
return i;
} else {
b = i;
}
if ((b - a) == 1) {
int result2 = name.compareTo(map[b].name);
if (result2 < 0) {
return a;
} else {
return b;
}
}
}
}
/**
* Return the slash count in a given string.
*/
private static final int slashCount(String name) {
int pos = -1;
int count = 0;
while ((pos = name.indexOf('/', pos + 1)) != -1) {
count++;
}
return count;
}
/**
* Insert into the right place in a sorted MapElement array, and prevent
* duplicates.
*/
private static final boolean insertMap
(BaseMapper.Mapping[] oldMap, BaseMapper.Mapping[] newMap, BaseMapper.Mapping newElement) {
int pos = find(oldMap, newElement.name);
if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
return false;
}
System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
newMap[pos + 1] = newElement;
System.arraycopy
(oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
return true;
}
/**
* Insert into the right place in a sorted MapElement array.
*/
private static final boolean removeMap
(BaseMapper.Mapping[] oldMap, BaseMapper.Mapping[] newMap, String name) {
int pos = find(oldMap, name);
if ((pos != -1) && (name.equals(oldMap[pos].name))) {
System.arraycopy(oldMap, 0, newMap, 0, pos);
System.arraycopy(oldMap, pos + 1, newMap, pos,
oldMap.length - pos - 1);
return true;
}
return false;
}
// ------------------------------------------------- MapElement Inner Class
protected static final class Host
extends BaseMapper.Mapping {
//Map<String, Context> contexts = new HashMap();
//Context rootContext;
public ContextList contextList = null;
}
// ------------------------------------------------ ContextList Inner Class
// Shared among host aliases.
protected static final class ContextList {
public BaseMapper.Context[] contexts = new BaseMapper.Context[0];
public int nesting = 0;
}
public static final class Context extends BaseMapper.Mapping {
Context(BaseMapper mapper) {
this.mapper = mapper;
}
public BaseMapper mapper;
public String[] welcomeResources = new String[0];
public FileConnector resources = null;
public BaseMapper.ServiceMapping defaultWrapper = null;
public BaseMapper.ServiceMapping[] exactWrappers = new BaseMapper.ServiceMapping[0];
public BaseMapper.ServiceMapping[] wildcardWrappers = new BaseMapper.ServiceMapping[0];
public BaseMapper.ServiceMapping[] extensionWrappers = new BaseMapper.ServiceMapping[0];
public int nesting = 0;
public void addWrapper(String path, HttpService service) {
mapper.addWrapper(this, path, service);
}
}
public static class ServiceMapping extends BaseMapper.Mapping {
public boolean jspWildCard = false;
// If set, the service will run in the selector thread ( should
// be non-blocking )
public boolean selectorThread = false;
}
protected static abstract class Mapping {
public String name = null;
public Object object = null;
public String toString() {
if (name == null || "".equals(name)) {
return "DEFAULT";
}
return name;
}
}
// ---------------------------------------------------- Context Inner Class
}