blob: 1cc9e6ab9d197f9cc652b9d4294352a29d5aff5d [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.dubbo.rpc.protocol.rest;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.metadata.extension.rest.api.PathMatcher;
import org.apache.dubbo.metadata.extension.rest.api.RestMethodMetadata;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.protocol.rest.exception.DoublePathCheckException;
import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* save the path & metadata info mapping
*/
public class PathAndInvokerMapper {
private static final ErrorTypeAwareLogger logger =
LoggerFactory.getErrorTypeAwareLogger(PathAndInvokerMapper.class);
private final Map<PathMatcher, InvokerAndRestMethodMetadataPair> pathToServiceMapContainPathVariable =
new ConcurrentHashMap<>();
private final Map<PathMatcher, InvokerAndRestMethodMetadataPair> pathToServiceMapNoPathVariable =
new ConcurrentHashMap<>();
// for http method compare 405
private final Map<PathMatcher, Set<String>> pathMatcherToHttpMethodMap = new HashMap<>();
/**
* deploy path metadata
*
* @param metadataMap
* @param invoker
*/
public void addPathAndInvoker(Map<PathMatcher, RestMethodMetadata> metadataMap, Invoker invoker) {
metadataMap.entrySet().stream().forEach(entry -> {
PathMatcher pathMatcher = entry.getKey();
if (pathMatcher.hasPathVariable()) {
addPathMatcherToPathMap(
pathMatcher,
pathToServiceMapContainPathVariable,
InvokerAndRestMethodMetadataPair.pair(invoker, entry.getValue()));
} else {
addPathMatcherToPathMap(
pathMatcher,
pathToServiceMapNoPathVariable,
InvokerAndRestMethodMetadataPair.pair(invoker, entry.getValue()));
}
});
}
/**
* get rest method metadata by path matcher
*
* @param pathMatcher
* @return
*/
public InvokerAndRestMethodMetadataPair getRestMethodMetadata(PathMatcher pathMatcher) {
// first search from pathToServiceMapNoPathVariable
if (pathToServiceMapNoPathVariable.containsKey(pathMatcher)) {
return pathToServiceMapNoPathVariable.get(pathMatcher);
}
// second search from pathToServiceMapContainPathVariable
if (pathToServiceMapContainPathVariable.containsKey(pathMatcher)) {
return pathToServiceMapContainPathVariable.get(pathMatcher);
}
return null;
}
/**
* undeploy path metadata
*
* @param pathMatcher
*/
public void removePath(PathMatcher pathMatcher) {
InvokerAndRestMethodMetadataPair containPathVariablePair =
pathToServiceMapContainPathVariable.remove(pathMatcher);
InvokerAndRestMethodMetadataPair unContainPathVariablePair = pathToServiceMapNoPathVariable.remove(pathMatcher);
logger.info("dubbo rest undeploy pathMatcher:" + pathMatcher
+ ", and path variable method is :"
+ (containPathVariablePair == null
? null
: containPathVariablePair.getRestMethodMetadata().getReflectMethod())
+ ", and no path variable method is :"
+ (unContainPathVariablePair == null
? null
: unContainPathVariablePair.getRestMethodMetadata().getReflectMethod()));
}
public void addPathMatcherToPathMap(
PathMatcher pathMatcher,
Map<PathMatcher, InvokerAndRestMethodMetadataPair> pathMatcherPairMap,
InvokerAndRestMethodMetadataPair invokerRestMethodMetadataPair) {
if (pathMatcherPairMap.containsKey(pathMatcher)) {
// cover the old service metadata when current interface is old interface & current method desc equals
// old`s method desc,else ,throw double check exception
InvokerAndRestMethodMetadataPair beforeMetadata = pathMatcherPairMap.get(pathMatcher);
// true when reExport
if (!invokerRestMethodMetadataPair.compareServiceMethod(beforeMetadata)) {
throw new DoublePathCheckException("dubbo rest double path check error, current path is: " + pathMatcher
+ " ,and service method is: "
+ invokerRestMethodMetadataPair.getRestMethodMetadata().getReflectMethod()
+ "before service method is: "
+ beforeMetadata.getRestMethodMetadata().getReflectMethod());
}
}
pathMatcherPairMap.put(pathMatcher, invokerRestMethodMetadataPair);
addPathMatcherToHttpMethodsMap(pathMatcher);
logger.info("dubbo rest deploy pathMatcher:" + pathMatcher + ", and service method is :"
+ invokerRestMethodMetadataPair.getRestMethodMetadata().getReflectMethod());
}
private void addPathMatcherToHttpMethodsMap(PathMatcher pathMatcher) {
PathMatcher newPathMatcher = PathMatcher.convertPathMatcher(pathMatcher);
if (!pathMatcherToHttpMethodMap.containsKey(newPathMatcher)) {
HashSet<String> httpMethods = new HashSet<>();
httpMethods.add(pathMatcher.getHttpMethod());
pathMatcherToHttpMethodMap.put(newPathMatcher, httpMethods);
}
Set<String> httpMethods = pathMatcherToHttpMethodMap.get(newPathMatcher);
httpMethods.add(newPathMatcher.getHttpMethod());
}
public boolean isHttpMethodAllowed(PathMatcher pathMatcher) {
PathMatcher newPathMatcher = PathMatcher.convertPathMatcher(pathMatcher);
if (!pathMatcherToHttpMethodMap.containsKey(newPathMatcher)) {
return false;
}
Set<String> httpMethods = pathMatcherToHttpMethodMap.get(newPathMatcher);
return httpMethods.contains(newPathMatcher.getHttpMethod());
}
public String pathHttpMethods(PathMatcher pathMatcher) {
PathMatcher newPathMatcher = PathMatcher.convertPathMatcher(pathMatcher);
if (!pathMatcherToHttpMethodMap.containsKey(newPathMatcher)) {
return null;
}
Set<String> httpMethods = pathMatcherToHttpMethodMap.get(newPathMatcher);
return httpMethods.toString();
}
}