/*
 * 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.server.handlers.proxy.mod_cluster;

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

import io.undertow.UndertowMessages;
import io.undertow.util.CopyOnWriteMap;
import io.undertow.util.PathMatcher;
import io.undertow.util.URLUtils;

The virtual host handler.
Author:Emanuel Muckenhuber
/** * The virtual host handler. * * @author Emanuel Muckenhuber */
public class VirtualHost { private static final String STRING_PATH_SEPARATOR = "/"; private final HostEntry defaultHandler = new HostEntry(STRING_PATH_SEPARATOR); private final ConcurrentMap<String, HostEntry> contexts = new CopyOnWriteMap<>();
lengths of all registered contexts
/** * lengths of all registered contexts */
private volatile int[] lengths = {}; protected VirtualHost() { // }
Matches a path against the registered handlers.
Params:
  • path – The relative path to match
Returns:The match match. This will never be null, however if none matched its value field will be
/** * 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 */
PathMatcher.PathMatch<HostEntry> match(String path){ int length = path.length(); final int[] lengths = this.lengths; for (int i = 0; i < lengths.length; ++i) { int pathLength = lengths[i]; if (pathLength == length) { HostEntry next = contexts.get(path); if (next != null) { return new PathMatcher.PathMatch<>(path, "", next); } } else if (pathLength < length) { char c = path.charAt(pathLength); if (c == '/') { String part = path.substring(0, pathLength); HostEntry next = contexts.get(part); if (next != null) { return new PathMatcher.PathMatch<>(part, path.substring(pathLength), next); } } } } if(defaultHandler.contexts.isEmpty()) { return new PathMatcher.PathMatch<>("", path, null); } return new PathMatcher.PathMatch<>("", path, defaultHandler); } public synchronized void registerContext(final String path, final String jvmRoute, final Context context) { if (path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } final String normalizedPath = URLUtils.normalizeSlashes(path); if (STRING_PATH_SEPARATOR.equals(normalizedPath)) { defaultHandler.contexts.put(jvmRoute, context); return; } boolean rebuild = false; HostEntry hostEntry = contexts.get(normalizedPath); if (hostEntry == null) { rebuild = true; hostEntry = new HostEntry(normalizedPath); contexts.put(normalizedPath, hostEntry); } assert !hostEntry.contexts.containsKey(jvmRoute); hostEntry.contexts.put(jvmRoute, context); if (rebuild) { buildLengths(); } } public synchronized void removeContext(final String path, final String jvmRoute, final Context context) { if (path == null || path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } final String normalizedPath = URLUtils.normalizeSlashes(path); if (STRING_PATH_SEPARATOR.equals(normalizedPath)) { defaultHandler.contexts.remove(jvmRoute, context); } final HostEntry hostEntry = contexts.get(normalizedPath); if (hostEntry != null) { if (hostEntry.contexts.remove(jvmRoute, context)) { if (hostEntry.contexts.isEmpty()) { contexts.remove(normalizedPath); buildLengths(); } } } } boolean isEmpty() { return contexts.isEmpty() && defaultHandler.contexts.isEmpty(); } private void buildLengths() { final Set<Integer> lengths = new TreeSet<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return -o1.compareTo(o2); } }); for (String p : contexts.keySet()) { lengths.add(p.length()); } int[] lengthArray = new int[lengths.size()]; int pos = 0; for (int i : lengths) { lengthArray[pos++] = i; } this.lengths = lengthArray; } static class HostEntry { // node > context private final ConcurrentMap<String, Context> contexts = new CopyOnWriteMap<>(); private final String contextPath; HostEntry(String contextPath) { this.contextPath = contextPath; } protected String getContextPath() { return contextPath; }
Get a context for a jvmRoute.
Params:
  • jvmRoute – the jvm route
/** * Get a context for a jvmRoute. * * @param jvmRoute the jvm route */
protected Context getContextForNode(final String jvmRoute) { return contexts.get(jvmRoute); }
Get list of nodes as jvmRoutes.
/** * Get list of nodes as jvmRoutes. */
protected Collection<String> getNodes() { return Collections.unmodifiableCollection(contexts.keySet()); }
Get all registered contexts.
/** * Get all registered contexts. */
protected Collection<Context> getContexts() { return Collections.unmodifiableCollection(contexts.values()); } } }