package com.networknt.registry;
import com.networknt.utility.Constants;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Created by stevehu on 2017-01-17.
*/
public class URLImpl implements URL {
private String protocol;
private String host;
private int port;
// interfaceName
private String path;
private Map<String, String> parameters;
private volatile transient Map<String, Number> numbers;
public URLImpl(String protocol, String host, int port, String path) {
this(protocol, host, port, path, new HashMap<>());
}
public URLImpl() {
}
public URLImpl(String protocol, String host, int port, String path, Map<String, String> parameters) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.path = path;
this.parameters = parameters;
}
public static URL valueOf(String url) {
String protocol = null;
String host = null;
int port = 0;
String path = null;
Map<String, String> parameters = new HashMap<>();
int i = url.indexOf("?"); // separator between body and parameters
if (i >= 0) {
String[] parts = url.substring(i + 1).split("&");
for (String part : parts) {
part = part.trim();
if (part.length() > 0) {
int j = part.indexOf('=');
if (j >= 0) {
parameters.put(part.substring(0, j), part.substring(j + 1));
} else {
parameters.put(part, part);
}
}
}
url = url.substring(0, i);
}
i = url.indexOf("://");
if (i >= 0) {
if (i == 0) throw new IllegalStateException("url missing protocol: \"" + url + "\"");
protocol = url.substring(0, i);
url = url.substring(i + 3);
} else {
i = url.indexOf(":/");
if (i >= 0) {
if (i == 0) throw new IllegalStateException("url missing protocol: \"" + url + "\"");
protocol = url.substring(0, i);
url = url.substring(i + 1);
}
}
i = url.indexOf("/");
if (i >= 0) {
path = url.substring(i + 1);
url = url.substring(0, i);
}
i = url.indexOf(":");
if (i >= 0 && i < url.length() - 1) {
port = Integer.parseInt(url.substring(i + 1));
url = url.substring(0, i);
}
if (url.length() > 0) host = url;
return new URLImpl(protocol, host, port, path, parameters);
}
private static String buildHostPortStr(String host, int defaultPort) {
if (defaultPort <= 0) {
return host;
}
int idx = host.indexOf(":");
if (idx < 0) {
return host + ":" + defaultPort;
}
int port = Integer.parseInt(host.substring(idx + 1));
if (port <= 0) {
return host.substring(0, idx + 1) + defaultPort;
}
return host;
}
@Override
public URL createCopy() {
Map<String, String> params = new HashMap<>();
if (this.parameters != null) {
params.putAll(this.parameters);
}
return new URLImpl(protocol, host, port, path, params);
}
@Override
public String getProtocol() {
return protocol;
}
@Override
public void setProtocol(String protocol) {
this.protocol = protocol;
}
@Override
public String getHost() {
return host;
}
@Override
public void setHost(String host) {
this.host = host;
}
@Override
public Integer getPort() {
return port;
}
@Override
public void setPort(int port) {
this.port = port;
}
@Override
public String getPath() {
return path;
}
@Override
public void setPath(String path) {
this.path = path;
}
@Override
public String getVersion() {
return getParameter(URLParamType.version.getName(), URLParamType.version.getValue());
}
@Override
public String getGroup() {
return getParameter(URLParamType.group.getName(), URLParamType.group.getValue());
}
@Override
public Map<String, String> getParameters() {
return parameters;
}
@Override
public void setParameters(Map<String, String> parameters) {
this.parameters = parameters;
}
@Override
public String getParameter(String name) {
return parameters.get(name);
}
@Override
public String getParameter(String name, String defaultValue) {
String value = getParameter(name);
if (value == null) {
return defaultValue;
}
return value;
}
@Override
public String getMethodParameter(String methodName, String paramDesc, String name) {
String value = getParameter(Constants.METHOD_CONFIG_PREFIX + methodName + "(" + paramDesc + ")." + name);
if (value == null || value.length() == 0) {
return getParameter(name);
}
return value;
}
@Override
public String getMethodParameter(String methodName, String paramDesc, String name, String defaultValue) {
String value = getMethodParameter(methodName, paramDesc, name);
if (value == null || value.length() == 0) {
return defaultValue;
}
return value;
}
@Override
public void addParameter(String name, String value) {
if (StringUtils.isEmpty(name) || StringUtils.isEmpty(value)) {
return;
}
parameters.put(name, value);
}
@Override
public void removeParameter(String name) {
if (name != null) {
parameters.remove(name);
}
}
@Override
public void addParameters(Map<String, String> params) {
parameters.putAll(params);
}
@Override
public void addParameterIfAbsent(String name, String value) {
if (hasParameter(name)) {
return;
}
parameters.put(name, value);
}
@Override
public Boolean getBooleanParameter(String name, boolean defaultValue) {
String value = getParameter(name);
if (value == null || value.length() == 0) {
return defaultValue;
}
return Boolean.parseBoolean(value);
}
@Override
public Boolean getMethodParameter(String methodName, String paramDesc, String name, boolean defaultValue) {
String value = getMethodParameter(methodName, paramDesc, name);
if (value == null || value.length() == 0) {
return defaultValue;
}
return Boolean.parseBoolean(value);
}
@Override
public Integer getIntParameter(String name, int defaultValue) {
Number n = getNumbers().get(name);
if (n != null) {
return n.intValue();
}
String value = parameters.get(name);
if (value == null || value.length() == 0) {
return defaultValue;
}
int i = Integer.parseInt(value);
getNumbers().put(name, i);
return i;
}
@Override
public Integer getMethodParameter(String methodName, String paramDesc, String name, int defaultValue) {
String key = methodName + "(" + paramDesc + ")." + name;
Number n = getNumbers().get(key);
if (n != null) {
return n.intValue();
}
String value = getMethodParameter(methodName, paramDesc, name);
if (value == null || value.length() == 0) {
return defaultValue;
}
int i = Integer.parseInt(value);
getNumbers().put(key, i);
return i;
}
@Override
public String getUri() {
return protocol + Constants.PROTOCOL_SEPARATOR + host + ":" + port +
Constants.PATH_SEPARATOR + path;
}
/**
* Return service identity, if two urls have the same identity, then same service
*
* @return the identity
*/
@Override
public String getIdentity() {
return protocol + Constants.PROTOCOL_SEPARATOR + host + ":" + port +
"/" + getParameter(URLParamType.group.getName(), URLParamType.group.getValue()) + "/" +
getPath() + "/" + getParameter(URLParamType.version.getName(), URLParamType.version.getValue()) +
"/" + getParameter(URLParamType.nodeType.getName(), URLParamType.nodeType.getValue());
}
/**
* check if this url can serve the refUrl.
*
* @param refUrl a URL object
* @return boolean true can serve
*/
@Override
public boolean canServe(URL refUrl) {
if (refUrl == null || !this.getPath().equals(refUrl.getPath())) {
return false;
}
if(!protocol.equals(refUrl.getProtocol())) {
return false;
}
if (!StringUtils.equals(this.getParameter(URLParamType.nodeType.getName()), Constants.NODE_TYPE_SERVICE)) {
return false;
}
String version = getParameter(URLParamType.version.getName(), URLParamType.version.getValue());
String refVersion = refUrl.getParameter(URLParamType.version.getName(), URLParamType.version.getValue());
if (!version.equals(refVersion)) {
return false;
}
// check serialize
String serialize = getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue());
String refSerialize = refUrl.getParameter(URLParamType.serialize.getName(), URLParamType.serialize.getValue());
if (!serialize.equals(refSerialize)) {
return false;
}
// Not going to check group as cross group call is needed
return true;
}
// TODO there is a trailing &, use string join instead StringBuilder.
@Override
public String toFullStr() {
StringBuilder builder = new StringBuilder();
builder.append(getUri()).append("?");
for (Map.Entry<String, String> entry : parameters.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
builder.append(name).append("=").append(value).append("&");
}
return builder.toString();
}
@Override
public String toString() {
return toSimpleString();
}
// protocol、host、port、path、group
@Override
public String toSimpleString() {
return getUri() + "?group=" + getGroup();
}
@Override
public boolean hasParameter(String key) {
return StringUtils.isNotBlank(getParameter(key));
}
/**
* comma separated host:port pairs, e.g. "127.0.0.1:3000"
*
* @return server port
*/
@Override
public String getServerPortStr() {
return buildHostPortStr(host, port);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
URLImpl url = (URLImpl) o;
if (port != url.getPort()) return false;
if (protocol != null ? !protocol.equals(url.getProtocol()) : url.getProtocol() != null) return false;
if (host != null ? !host.equals(url.getHost()) : url.getHost() != null) return false;
if (path != null ? !path.equals(url.getPath()) : url.getPath() != null) return false;
if (parameters != null ? !parameters.equals(url.getParameters()) : url.getParameters() != null) return false;
return parameters != null ? parameters.equals(url.getParameters()) : url.getParameters() == null;
}
@Override
public int hashCode() {
int result = protocol != null ? protocol.hashCode() : 0;
result = 31 * result + (host != null ? host.hashCode() : 0);
result = 31 * result + port;
result = 31 * result + (path != null ? path.hashCode() : 0);
result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
result = 31 * result + (numbers != null ? numbers.hashCode() : 0);
return result;
}
private Map<String, Number> getNumbers() {
if (numbers == null) {
// create a new map.
numbers = new ConcurrentHashMap<>();
}
return numbers;
}
}