blob: 3fa571d7e8b8e99df45b2f900a058527d7886b97 [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.metadata.extension.rest.api;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
/**
* for http request path match
*/
public class PathMatcher {
private static final String SEPARATOR = "/";
private String path;
private String version; // service version
private String group; // service group
private Integer port; // service port
private String[] pathSplits;
private boolean hasPathVariable;
private String contextPath;
private String httpMethod;
// for provider http method compare,http 405
private boolean needCompareHttpMethod = true;
// compare method directly (for get Invoker by method)
private boolean needCompareServiceMethod = false;
// service method
private Method method;
public PathMatcher(String path) {
this(path, null, null, null);
}
public PathMatcher(String path, String version, String group, Integer port) {
this.path = path;
dealPathVariable(path);
this.version = version;
this.group = group;
this.port = (port == null || port == -1 || port == 0) ? null : port;
}
public PathMatcher(String path, String version, String group, Integer port, String httpMethod) {
this(path, version, group, port);
setHttpMethod(httpMethod);
}
public PathMatcher(Method method) {
this.method = method;
}
private void dealPathVariable(String path) {
if (path == null) {
return;
}
this.pathSplits = path.split(SEPARATOR);
for (String pathSplit : pathSplits) {
if (isPlaceHold(pathSplit)) {
hasPathVariable = true;
break;
}
}
}
private void setPath(String path) {
this.path = path;
}
public void setVersion(String version) {
this.version = version;
}
public void setGroup(String group) {
this.group = group;
}
public void setPort(Integer port) {
this.port = port;
}
public void setContextPath(String contextPath) {
contextPath = contextPathFormat(contextPath);
this.contextPath = contextPath;
setPath(contextPath + path);
dealPathVariable(path);
}
public static PathMatcher getInvokeCreatePathMatcher(
String path, String version, String group, Integer port, String method) {
return new PathMatcher(path, version, group, port, method).compareHttpMethod(false);
}
public static PathMatcher getInvokeCreatePathMatcher(Method serviceMethod) {
return new PathMatcher(serviceMethod).setNeedCompareServiceMethod(true);
}
public static PathMatcher convertPathMatcher(PathMatcher pathMatcher) {
return getInvokeCreatePathMatcher(
pathMatcher.path, pathMatcher.version, pathMatcher.group, pathMatcher.port, pathMatcher.httpMethod);
}
public boolean hasPathVariable() {
return hasPathVariable;
}
public Integer getPort() {
return port;
}
public String getHttpMethod() {
return httpMethod;
}
public PathMatcher setHttpMethod(String httpMethod) {
this.httpMethod = httpMethod;
return this;
}
public PathMatcher compareHttpMethod(boolean needCompareHttpMethod) {
this.needCompareHttpMethod = needCompareHttpMethod;
return this;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
private PathMatcher setNeedCompareServiceMethod(boolean needCompareServiceMethod) {
this.needCompareServiceMethod = needCompareServiceMethod;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PathMatcher that = (PathMatcher) o;
return serviceMethodEqual(that, this) || pathMatch(that);
}
private boolean pathMatch(PathMatcher that) {
return (!that.needCompareServiceMethod && !needCompareServiceMethod) // no need service method compare
&& pathEqual(that) // path compare
&& Objects.equals(version, that.version) // service version compare
&& httpMethodMatch(that) // http method compare
&& Objects.equals(group, that.group)
&& Objects.equals(port, that.port);
}
/**
* it is needed to compare http method when one of needCompareHttpMethod is true,and don`t compare when both needCompareHttpMethod are false
*
* @param that
* @return
*/
private boolean httpMethodMatch(PathMatcher that) {
return !that.needCompareHttpMethod || !this.needCompareHttpMethod
? true
: Objects.equals(this.httpMethod, that.httpMethod);
}
private boolean serviceMethodEqual(PathMatcher thatPathMatcher, PathMatcher thisPathMatcher) {
Method thatMethod = thatPathMatcher.method;
Method thisMethod = thisPathMatcher.method;
return thatMethod != null
&& thisMethod != null
&& (thatPathMatcher.needCompareServiceMethod || thisPathMatcher.needCompareServiceMethod)
&& thisMethod.getName().equals(thatMethod.getName())
&& Arrays.equals(thisMethod.getParameterTypes(), thatMethod.getParameterTypes());
}
@Override
public int hashCode() {
return Objects.hash(version, group, port);
}
private boolean pathEqual(PathMatcher pathMatcher) {
// path is null return false directly
if (this.path == null || pathMatcher.path == null) {
return false;
}
// no place hold
if (!pathMatcher.hasPathVariable) {
return this.path.equals(pathMatcher.path);
}
String[] pathSplits = pathMatcher.pathSplits;
String[] thisPathSplits = this.pathSplits;
if (thisPathSplits.length != pathSplits.length) {
return false;
}
for (int i = 0; i < pathSplits.length; i++) {
boolean equals = thisPathSplits[i].equals(pathSplits[i]);
if (equals) {
continue;
} else {
if (placeHoldCompare(pathSplits[i], thisPathSplits[i])) {
continue;
} else {
return false;
}
}
}
return true;
}
private boolean placeHoldCompare(String pathSplit, String pathToCompare) {
boolean startAndEndEqual = isPlaceHold(pathSplit) || isPlaceHold(pathToCompare);
// start { end }
if (!startAndEndEqual) {
return false;
}
// exclude {}
boolean lengthCondition = pathSplit.length() >= 3 || pathToCompare.length() >= 3;
if (!lengthCondition) {
return false;
}
return true;
}
private boolean isPlaceHold(String pathSplit) {
return pathSplit.startsWith("{") && pathSplit.endsWith("}");
}
private String contextPathFormat(String contextPath) {
if (contextPath == null || contextPath.equals(SEPARATOR) || contextPath.length() == 0) {
return "";
}
return pathFormat(contextPath);
}
private String pathFormat(String path) {
if (path.startsWith(SEPARATOR)) {
return path;
} else {
return SEPARATOR + path;
}
}
@Override
public String toString() {
return "PathMatcher{" + "path='"
+ path + '\'' + ", version='"
+ version + '\'' + ", group='"
+ group + '\'' + ", port="
+ port + ", hasPathVariable="
+ hasPathVariable + ", contextPath='"
+ contextPath + '\'' + ", httpMethod='"
+ httpMethod + '\'' + '}';
}
}