/*
* Copyright 2016 LINE Corporation
*
* LINE Corporation 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 com.linecorp.armeria.client.endpoint;
import static java.util.Objects.requireNonNull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import com.google.common.base.Ascii;
import com.linecorp.armeria.client.Endpoint;
/**
* An in-memory registry of server groups.
*/
public final class EndpointGroupRegistry {
private static final Pattern GROUP_NAME_PATTERN = Pattern.compile("^[-_.0-9a-z]+$");
private static final Map<String, EndpointSelector> serverGroups = new ConcurrentHashMap<>();
/**
* Registers the specified {@link EndpointGroup}. If there's already an {@link EndpointGroup} with the
* specified {@code groupName}, this method will replace it with the new one.
*
* @param groupName the case-insensitive name of the {@link EndpointGroup} that matches
* the regular expression {@code /^[-_.0-9a-zA-Z]+$/}
* @param endpointGroup the {@link EndpointGroup} to register
* @param endpointSelectionStrategy the {@link EndpointSelectionStrategy} of the registered group
*
* @return {@code true} if there was no {@link EndpointGroup} with the specified {@code groupName}.
* {@code false} if there was already an {@link EndpointGroup} with the specified {@code groupName}
* and it has been replaced with the new one.
*/
public static boolean register(String groupName, EndpointGroup endpointGroup,
EndpointSelectionStrategy endpointSelectionStrategy) {
groupName = normalizeGroupName(groupName);
if (!GROUP_NAME_PATTERN.matcher(groupName).matches()) {
throw new IllegalArgumentException(
"groupName: " + groupName + " (expected: " + GROUP_NAME_PATTERN.pattern() + ')');
}
requireNonNull(endpointGroup, "group");
requireNonNull(endpointSelectionStrategy, "endpointSelectionStrategy");
final EndpointSelector oldSelector = serverGroups.put(
groupName, endpointSelectionStrategy.newSelector(endpointGroup));
return oldSelector == null;
}
/**
* Unregisters the {@link EndpointGroup} with the specified case-insensitive {@code groupName}.
* Note that this is potentially a dangerous operation; make sure the {@code groupName} of the unregistered
* {@link EndpointGroup} is not in use by any clients.
*
* @return {@code true} if the {@link EndpointGroup} with the specified {@code groupName} has been removed.
* {@code false} if there's no such {@link EndpointGroup} in the registry.
*/
public static boolean unregister(String groupName) {
groupName = normalizeGroupName(groupName);
return serverGroups.remove(groupName) != null;
}
/**
* Returns the {@link EndpointSelector} for the specified case-insensitive {@code groupName}.
*
* @return the {@link EndpointSelector}, or {@code null} if {@code groupName} has not been registered yet.
*/
public static EndpointSelector getNodeSelector(String groupName) {
groupName = normalizeGroupName(groupName);
return serverGroups.get(groupName);
}
/**
* Get the {@link EndpointGroup} for the specified case-insensitive {@code groupName}.
*
* @return the {@link EndpointSelector}, or {@code null} if {@code groupName} has not been registered yet.
*/
public static EndpointGroup get(String groupName) {
groupName = normalizeGroupName(groupName);
EndpointSelector endpointSelector = serverGroups.get(groupName);
if (endpointSelector == null) {
return null;
}
return endpointSelector.group();
}
/**
* Selects an {@link Endpoint} from the {@link EndpointGroup} associated with the specified
* case-insensitive {@code groupName}.
*/
public static Endpoint selectNode(String groupName) {
groupName = normalizeGroupName(groupName);
EndpointSelector endpointSelector = getNodeSelector(groupName);
if (endpointSelector == null) {
throw new EndpointGroupException("non-existent EndpointGroup: " + groupName);
}
return endpointSelector.select();
}
private static String normalizeGroupName(String groupName) {
return Ascii.toLowerCase(requireNonNull(groupName, "groupName"));
}
private EndpointGroupRegistry() {}
}