/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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 io.undertow.servlet.handlers;

import io.undertow.UndertowMessages;
import io.undertow.util.SubstringMap;

import java.util.HashMap;
import java.util.Map;

Class that maintains the complete set of servlet path matches.
Author:Stuart Douglas
/** * Class that maintains the complete set of servlet path matches. * * * @author Stuart Douglas */
class ServletPathMatchesData { private final Map<String, ServletPathMatch> exactPathMatches; private final SubstringMap<PathMatch> prefixMatches; private final Map<String, ServletChain> nameMatches; ServletPathMatchesData(final Map<String, ServletChain> exactPathMatches, final SubstringMap<PathMatch> prefixMatches, final Map<String, ServletChain> nameMatches) { this.prefixMatches = prefixMatches; this.nameMatches = nameMatches; Map<String, ServletPathMatch> newExactPathMatches = new HashMap<>(); for (Map.Entry<String, ServletChain> entry : exactPathMatches.entrySet()) { newExactPathMatches.put(entry.getKey(), new ServletPathMatch(entry.getValue(), entry.getKey(), entry.getValue().isDefaultServletMapping())); } this.exactPathMatches = newExactPathMatches; } public ServletChain getServletHandlerByName(final String name) { return nameMatches.get(name); } public ServletPathMatch getServletHandlerByExactPath(final String path) { return exactPathMatches.get(path); } public ServletPathMatch getServletHandlerByPath(final String path) { ServletPathMatch exact = exactPathMatches.get(path); if (exact != null) { return exact; } SubstringMap.SubstringMatch<PathMatch> match = prefixMatches.get(path, path.length()); if (match != null) { return handleMatch(path, match.getValue(), path.lastIndexOf('.')); } int extensionPos = -1; for (int i = path.length() - 1; i >= 0; --i) { final char c = path.charAt(i); if (c == '/') { match = prefixMatches.get(path, i); if (match != null) { return handleMatch(path, match.getValue(), extensionPos); } } else if (c == '.' && extensionPos == -1) { extensionPos = i; } } //this should never happen //as the default servlet is aways registered under /* throw UndertowMessages.MESSAGES.servletPathMatchFailed(); } private ServletPathMatch handleMatch(final String path, final PathMatch match, final int extensionPos) { if (match.extensionMatches.isEmpty()) { return new ServletPathMatch(match.defaultHandler, path, match.requireWelcomeFileMatch); } if (extensionPos == -1) { return new ServletPathMatch(match.defaultHandler, path, match.requireWelcomeFileMatch); } final String ext; ext = path.substring(extensionPos + 1, path.length()); ServletChain handler = match.extensionMatches.get(ext); if (handler != null) { return new ServletPathMatch(handler, path, handler.getManagedServlet().getServletInfo().isRequireWelcomeFileMapping()); } return new ServletPathMatch(match.defaultHandler, path, match.requireWelcomeFileMatch); } public static Builder builder() { return new Builder(); } public static final class Builder { private final Map<String, ServletChain> exactPathMatches = new HashMap<>(); private final SubstringMap<PathMatch> prefixMatches = new SubstringMap<PathMatch>(); private final Map<String, ServletChain> nameMatches = new HashMap<>(); public void addExactMatch(final String exactMatch, final ServletChain match) { exactPathMatches.put(exactMatch, match); } public void addPrefixMatch(final String prefix, final ServletChain match, final boolean requireWelcomeFileMatch) { SubstringMap.SubstringMatch<PathMatch> mt = prefixMatches.get(prefix); PathMatch m; if (mt == null) { prefixMatches.put(prefix, m = new PathMatch(match)); } else { m = mt.getValue(); } m.defaultHandler = match; m.requireWelcomeFileMatch = requireWelcomeFileMatch; } public void addExtensionMatch(final String prefix, final String extension, final ServletChain match) { SubstringMap.SubstringMatch<PathMatch> mt = prefixMatches.get(prefix); PathMatch m; if (mt == null) { prefixMatches.put(prefix, m = new PathMatch(null)); } else { m = mt.getValue(); } m.extensionMatches.put(extension, match); } public void addNameMatch(final String name, final ServletChain match) { nameMatches.put(name, match); } public ServletPathMatchesData build() { return new ServletPathMatchesData(exactPathMatches, prefixMatches, nameMatches); } } private static class PathMatch { private final Map<String, ServletChain> extensionMatches = new HashMap<>(); private volatile ServletChain defaultHandler; private volatile boolean requireWelcomeFileMatch; PathMatch(final ServletChain defaultHandler) { this.defaultHandler = defaultHandler; } } }