All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.util.PathMatcher Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * 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.util;

import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;

import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentMap;

/**
 * Handler that dispatches to a given handler based of a prefix match of the path.
 * 

* This only matches a single level of a request, e.g if you have a request that takes the form: *

* /foo/bar *

* * @author Stuart Douglas */ public class PathMatcher { private static final String STRING_PATH_SEPARATOR = "/"; private volatile T defaultHandler; private final SubstringMap paths = new SubstringMap<>(); private final ConcurrentMap exactPathMatches = new CopyOnWriteMap<>(); /** * lengths of all registered paths */ private volatile int[] lengths = {}; public PathMatcher(final T defaultHandler) { this.defaultHandler = defaultHandler; } public PathMatcher() { } public Set getExactPathMatchesSet(){ return Collections.unmodifiableSet(exactPathMatches.keySet()); } public Set getPathMatchesSet(){ return Collections.unmodifiableSet(paths.toMap().keySet()); } /** * Matches a path against the registered handlers. * @param path The relative path to match * @return The match match. This will never be null, however if none matched its value field will be */ public PathMatch match(String path){ if (!exactPathMatches.isEmpty()) { T match = getExactPath(path); if (match != null) { UndertowLogger.REQUEST_LOGGER.debugf("Matched exact path %s", path); return new PathMatch<>(path, "", match); } } int length = path.length(); final int[] lengths = this.lengths; for (int i = 0; i < lengths.length; ++i) { int pathLength = lengths[i]; if (pathLength == length) { SubstringMap.SubstringMatch next = paths.get(path, length); if (next != null) { UndertowLogger.REQUEST_LOGGER.debugf("Matched prefix path %s for path %s", next.getKey(), path); return new PathMatch<>(path, "", next.getValue()); } } else if (pathLength < length) { char c = path.charAt(pathLength); if (c == '/') { SubstringMap.SubstringMatch next = paths.get(path, pathLength); if (next != null) { UndertowLogger.REQUEST_LOGGER.debugf("Matched prefix path %s for path %s", next.getKey(), path); return new PathMatch<>(next.getKey(), path.substring(pathLength), next.getValue()); } } } } UndertowLogger.REQUEST_LOGGER.debugf("Matched default handler path %s", path); return new PathMatch<>("", path, defaultHandler); } /** * Adds a path prefix and a handler for that path. If the path does not start * with a / then one will be prepended. *

* The match is done on a prefix bases, so registering /foo will also match /foo/bar. Exact * path matches are taken into account first. *

* If / is specified as the path then it will replace the default handler. * * @param path The path * @param handler The handler */ public synchronized PathMatcher addPrefixPath(final String path, final T handler) { if (path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } final String normalizedPath = URLUtils.normalizeSlashes(path); if (PathMatcher.STRING_PATH_SEPARATOR.equals(normalizedPath)) { this.defaultHandler = handler; return this; } paths.put(normalizedPath, handler); buildLengths(); return this; } public synchronized PathMatcher addExactPath(final String path, final T handler) { if (path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } exactPathMatches.put(URLUtils.normalizeSlashes(path), handler); return this; } public T getExactPath(final String path) { return exactPathMatches.get(URLUtils.normalizeSlashes(path)); } public T getPrefixPath(final String path) { final String normalizedPath = URLUtils.normalizeSlashes(path); // enable the prefix path mechanism to return the default handler SubstringMap.SubstringMatch match = paths.get(normalizedPath); if (PathMatcher.STRING_PATH_SEPARATOR.equals(normalizedPath) && match == null) { return this.defaultHandler; } if(match == null) { return null; } // return the value for the given path return match.getValue(); } private void buildLengths() { final Set lengths = new TreeSet<>(new Comparator() { @Override public int compare(Integer o1, Integer o2) { return -o1.compareTo(o2); } }); for (String p : paths.keys()) { lengths.add(p.length()); } int[] lengthArray = new int[lengths.size()]; int pos = 0; for (int i : lengths) { lengthArray[pos++] = i; } this.lengths = lengthArray; } @Deprecated public synchronized PathMatcher removePath(final String path) { return removePrefixPath(path); } public synchronized PathMatcher removePrefixPath(final String path) { if (path == null || path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } final String normalizedPath = URLUtils.normalizeSlashes(path); if (PathMatcher.STRING_PATH_SEPARATOR.equals(normalizedPath)) { defaultHandler = null; return this; } paths.remove(normalizedPath); buildLengths(); return this; } public synchronized PathMatcher removeExactPath(final String path) { if (path == null || path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } exactPathMatches.remove(URLUtils.normalizeSlashes(path)); return this; } public synchronized PathMatcher clearPaths() { paths.clear(); exactPathMatches.clear(); this.lengths = new int[0]; defaultHandler = null; return this; } public Map getPaths() { return paths.toMap(); } public static final class PathMatch { private final String matched; private final String remaining; private final T value; public PathMatch(String matched, String remaining, T value) { this.matched = matched; this.remaining = remaining; this.value = value; } public String getRemaining() { return remaining; } public String getMatched() { return matched; } public T getValue() { return value; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy