[3.0-URL] Refactor URLParam with static reference (#7353)
* refact URLParam * impl unsupported method * fix NPE * performance tuning * add ut * introduce constant * remove unnecessary new * change value to array * add comment fix default value not changed * fix array outbound * add empty constructor for URLParam to adapt hessian deserialize
This commit is contained in:
parent
ea933320e1
commit
b9bca6a089
|
|
@ -177,7 +177,7 @@ class URL implements Serializable {
|
|||
}
|
||||
|
||||
this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
|
||||
this.urlParam = new URLParam(parameters);
|
||||
this.urlParam = URLParam.parse(parameters);
|
||||
}
|
||||
|
||||
protected URL(String protocol,
|
||||
|
|
@ -194,7 +194,7 @@ class URL implements Serializable {
|
|||
}
|
||||
|
||||
this.urlAddress = new PathURLAddress(protocol, username, password, path, host, port);
|
||||
this.urlParam = new URLParam(parameters);
|
||||
this.urlParam = URLParam.parse(parameters);
|
||||
}
|
||||
|
||||
public static URL cacheableValueOf(String url) {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ public class ServiceConfigURL extends URL {
|
|||
int port,
|
||||
String path,
|
||||
Map<String, String> parameters) {
|
||||
this(new PathURLAddress(protocol, username, password, path, host, port), new URLParam(parameters), null);
|
||||
this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), null);
|
||||
}
|
||||
|
||||
public ServiceConfigURL(String protocol,
|
||||
|
|
@ -56,7 +56,7 @@ public class ServiceConfigURL extends URL {
|
|||
String path,
|
||||
Map<String, String> parameters,
|
||||
Map<String, Object> attributes) {
|
||||
this(new PathURLAddress(protocol, username, password, path, host, port), new URLParam(parameters), attributes);
|
||||
this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), attributes);
|
||||
}
|
||||
|
||||
protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
|
||||
|
|
|
|||
|
|
@ -18,45 +18,125 @@ package org.apache.dubbo.common.url.component;
|
|||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.URLStrParser;
|
||||
import org.apache.dubbo.common.url.component.param.DynamicParamTable;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
import org.apache.dubbo.common.utils.StringUtils;
|
||||
|
||||
import org.eclipse.collections.impl.map.mutable.UnifiedMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_KEY_PREFIX;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY;
|
||||
|
||||
/**
|
||||
* A class which store parameters for {@link URL}
|
||||
* <br/>
|
||||
* Using {@link DynamicParamTable} to compress common keys (i.e. side, version)
|
||||
* <br/>
|
||||
* {@link DynamicParamTable} allow to use only two integer value named `key` and
|
||||
* `value-offset` to find a unique string to string key-pair. Also, `value-offset`
|
||||
* is not required if the real value is the default value.
|
||||
* <br/>
|
||||
* URLParam should operate as Copy-On-Write, each modify actions will return a new Object
|
||||
*
|
||||
* @since 3.0
|
||||
*/
|
||||
public class URLParam implements Serializable {
|
||||
private static final long serialVersionUID = -1985165475234910535L;
|
||||
|
||||
/**
|
||||
* Maximum size of key-pairs requested using array moving to add into URLParam.
|
||||
* If user request like addParameter for only one key-pair, adding value into a array
|
||||
* on moving is more efficient. However when add more than ADD_PARAMETER_ON_MOVE_THRESHOLD
|
||||
* size of key-pairs, recover compressed array back to map can reduce operation count
|
||||
* when putting objects.
|
||||
*/
|
||||
private static final int ADD_PARAMETER_ON_MOVE_THRESHOLD = 1;
|
||||
|
||||
/**
|
||||
* the original parameters string, empty if parameters have been modified or init by {@link Map}
|
||||
*/
|
||||
private final String rawParam;
|
||||
private final Map<String, String> params;
|
||||
|
||||
/**
|
||||
* using bit to save if index exist even if value is default value
|
||||
*/
|
||||
private final BitSet KEY;
|
||||
|
||||
/**
|
||||
* using bit to save if value is default value (reduce VALUE size)
|
||||
*/
|
||||
private final BitSet DEFAULT_KEY;
|
||||
|
||||
/**
|
||||
* a compressed (those key not exist or value is default value are bring removed in this array) array which contains value-offset
|
||||
*/
|
||||
private final int[] VALUE;
|
||||
|
||||
/**
|
||||
* store extra parameters which key not match in {@link DynamicParamTable}
|
||||
*/
|
||||
private final Map<String, String> EXTRA_PARAMS;
|
||||
|
||||
//cache
|
||||
private transient Map<String, Map<String, String>> methodParameters;
|
||||
private transient long timestamp;
|
||||
|
||||
public URLParam(Map<String, String> params) {
|
||||
this(params, null);
|
||||
private final static URLParam EMPTY_PARAM = new URLParam(new BitSet(0), Collections.emptyMap(), Collections.emptyMap(), "");
|
||||
|
||||
private URLParam() {
|
||||
this.rawParam = null;
|
||||
this.KEY = null;
|
||||
this.DEFAULT_KEY = null;
|
||||
this.VALUE = null;
|
||||
this.EXTRA_PARAMS = null;
|
||||
}
|
||||
|
||||
public URLParam(Map<String, String> params, String rawParam) {
|
||||
this.params = Collections.unmodifiableMap((params == null ? new HashMap<>() : new HashMap<>(params)));
|
||||
private URLParam(BitSet key, Map<Integer, Integer> value, Map<String, String> extraParams, String rawParam) {
|
||||
this.KEY = key;
|
||||
this.DEFAULT_KEY = new BitSet(KEY.size());
|
||||
this.VALUE = new int[value.size()];
|
||||
|
||||
// compress VALUE
|
||||
for (int i = key.nextSetBit(0), offset = 0; i >= 0; i = key.nextSetBit(i + 1)) {
|
||||
if (value.containsKey(i)) {
|
||||
VALUE[offset++] = value.get(i);
|
||||
} else {
|
||||
DEFAULT_KEY.set(i);
|
||||
}
|
||||
}
|
||||
|
||||
this.EXTRA_PARAMS = Collections.unmodifiableMap((extraParams == null ? new HashMap<>() : new HashMap<>(extraParams)));
|
||||
this.rawParam = rawParam;
|
||||
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private URLParam(BitSet key, BitSet defaultKey, int[] value, Map<String, String> extraParams, String rawParam) {
|
||||
this.KEY = key;
|
||||
this.DEFAULT_KEY = defaultKey;
|
||||
|
||||
this.VALUE = value;
|
||||
|
||||
this.EXTRA_PARAMS = Collections.unmodifiableMap((extraParams == null ? new HashMap<>() : new HashMap<>(extraParams)));
|
||||
this.rawParam = rawParam;
|
||||
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Map<String, Map<String, String>> getMethodParameters() {
|
||||
// TODO: delete it
|
||||
if (methodParameters == null) {
|
||||
methodParameters = Collections.unmodifiableMap(initMethodParameters(this.params));
|
||||
methodParameters = Collections.unmodifiableMap(initMethodParameters(getParameters()));
|
||||
}
|
||||
return methodParameters;
|
||||
}
|
||||
|
|
@ -94,45 +174,242 @@ public class URLParam implements Serializable {
|
|||
return methodParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* An embedded Map adapt to URLParam
|
||||
* <br/>
|
||||
* copy-on-write mode, urlParam reference will be changed after modify actions.
|
||||
* If wishes to get the result after modify, please use {@link URLParamMap#getUrlParam()}
|
||||
*/
|
||||
public static class URLParamMap implements Map<String, String> {
|
||||
private URLParam urlParam;
|
||||
|
||||
public URLParamMap(URLParam urlParam) {
|
||||
this.urlParam = urlParam;
|
||||
}
|
||||
|
||||
public static class Node implements Map.Entry<String, String> {
|
||||
private final String key;
|
||||
private String value;
|
||||
|
||||
public Node(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String setValue(String value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Node node = (Node) o;
|
||||
return Objects.equals(key, node.key) && Objects.equals(value, node.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return urlParam.KEY.cardinality() + urlParam.EXTRA_PARAMS.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
if (key instanceof String) {
|
||||
return urlParam.hasParameter((String) key);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return values().contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(Object key) {
|
||||
if (key instanceof String) {
|
||||
return urlParam.getParameter((String) key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String put(String key, String value) {
|
||||
String previous = urlParam.getParameter(key);
|
||||
urlParam = urlParam.addParameter(key, value);
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String remove(Object key) {
|
||||
if (key instanceof String) {
|
||||
String previous = urlParam.getParameter((String) key);
|
||||
urlParam = urlParam.removeParameters((String) key);
|
||||
return previous;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends String> m) {
|
||||
urlParam = urlParam.addParameters((Map<String, String>) m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
urlParam = urlParam.clearParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
Set<String> set = new HashSet<>((int) ((urlParam.VALUE.length + urlParam.EXTRA_PARAMS.size()) / 0.75) + 1);
|
||||
for (int i = urlParam.KEY.nextSetBit(0); i >= 0; i = urlParam.KEY.nextSetBit(i + 1)) {
|
||||
set.add(DynamicParamTable.getKey(i));
|
||||
}
|
||||
for (Entry<String, String> entry : urlParam.EXTRA_PARAMS.entrySet()) {
|
||||
set.add(entry.getKey());
|
||||
}
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> values() {
|
||||
Set<String> set = new HashSet<>((int) ((urlParam.VALUE.length + urlParam.EXTRA_PARAMS.size()) / 0.75) + 1);
|
||||
for (int i = urlParam.KEY.nextSetBit(0); i >= 0; i = urlParam.KEY.nextSetBit(i + 1)) {
|
||||
String value;
|
||||
if (urlParam.DEFAULT_KEY.get(i)) {
|
||||
value = DynamicParamTable.getDefaultValue(i);
|
||||
} else {
|
||||
int offset = urlParam.keyIndexToOffset(i);
|
||||
value = DynamicParamTable.getValue(i, offset);
|
||||
}
|
||||
set.add(value);
|
||||
}
|
||||
|
||||
for (Entry<String, String> entry : urlParam.EXTRA_PARAMS.entrySet()) {
|
||||
set.add(entry.getValue());
|
||||
}
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, String>> entrySet() {
|
||||
Set<Entry<String, String>> set = new HashSet<>((int) ((urlParam.KEY.cardinality() + urlParam.EXTRA_PARAMS.size()) / 0.75) + 1);
|
||||
for (int i = urlParam.KEY.nextSetBit(0); i >= 0; i = urlParam.KEY.nextSetBit(i + 1)) {
|
||||
String value;
|
||||
if (urlParam.DEFAULT_KEY.get(i)) {
|
||||
value = DynamicParamTable.getDefaultValue(i);
|
||||
} else {
|
||||
int offset = urlParam.keyIndexToOffset(i);
|
||||
value = DynamicParamTable.getValue(i, offset);
|
||||
}
|
||||
set.add(new Node(DynamicParamTable.getKey(i), value));
|
||||
}
|
||||
|
||||
for (Entry<String, String> entry : urlParam.EXTRA_PARAMS.entrySet()) {
|
||||
set.add(new Node(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
public URLParam getUrlParam() {
|
||||
return urlParam;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
URLParamMap that = (URLParamMap) o;
|
||||
return Objects.equals(urlParam, that.urlParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(urlParam);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Map like URLParam
|
||||
*
|
||||
* @return a {@link URLParamMap} adapt to URLParam
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
return params;
|
||||
return new URLParamMap(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parameters to a new URLParam.
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @return A new URLParam
|
||||
*/
|
||||
public URLParam addParameter(String key, String value) {
|
||||
if (StringUtils.isEmpty(key)
|
||||
|| StringUtils.isEmpty(value)) {
|
||||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
// if value doesn't change, return immediately
|
||||
if (value.equals(getParameters().get(key))) { // value != null
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, String> map = new HashMap<>(getParameters());
|
||||
map.put(key, value);
|
||||
return new URLParam(map, rawParam);
|
||||
return addParameters(Collections.singletonMap(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add absent parameters to a new URLParam.
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
* @return A new URLParam
|
||||
*/
|
||||
public URLParam addParameterIfAbsent(String key, String value) {
|
||||
if (StringUtils.isEmpty(key)
|
||||
|| StringUtils.isEmpty(value)) {
|
||||
if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) {
|
||||
return this;
|
||||
}
|
||||
if (hasParameter(key)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, String> map = new HashMap<>(getParameters());
|
||||
map.put(key, value);
|
||||
|
||||
return new URLParam(map, rawParam);
|
||||
return addParametersIfAbsent(Collections.singletonMap(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parameters to a new url.
|
||||
* Add parameters to a new URLParam.
|
||||
* If key-pair is present, this will cover it.
|
||||
*
|
||||
* @param parameters parameters in key-value pairs
|
||||
* @return A new URL
|
||||
* @return A new URLParam
|
||||
*/
|
||||
public URLParam addParameters(Map<String, String> parameters) {
|
||||
if (CollectionUtils.isEmptyMap(parameters)) {
|
||||
|
|
@ -159,53 +436,271 @@ public class URLParam implements Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
Map<String, String> map = new HashMap<>((int)(getParameters().size() + parameters.size() / 0.75f) + 1);
|
||||
map.putAll(getParameters());
|
||||
map.putAll(parameters);
|
||||
return new URLParam(map, rawParam);
|
||||
return doAddParameters(parameters, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add absent parameters to a new URLParam.
|
||||
*
|
||||
* @param parameters parameters in key-value pairs
|
||||
* @return A new URL
|
||||
*/
|
||||
public URLParam addParametersIfAbsent(Map<String, String> parameters) {
|
||||
if (CollectionUtils.isEmptyMap(parameters)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, String> map = new HashMap<>((int)(getParameters().size() + parameters.size() / 0.75f) + 1);
|
||||
map.putAll(parameters);
|
||||
map.putAll(getParameters());
|
||||
return new URLParam(map, rawParam);
|
||||
return doAddParameters(parameters, true);
|
||||
}
|
||||
|
||||
private URLParam doAddParameters(Map<String, String> parameters, boolean skipIfPresent) {
|
||||
// lazy init, null if no modify
|
||||
BitSet newKey = null;
|
||||
BitSet defaultKey = null;
|
||||
int[] newValueArray = null;
|
||||
Map<Integer, Integer> newValueMap = null;
|
||||
Map<String, String> newExtraParams = null;
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
if (skipIfPresent && hasParameter(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
Integer keyIndex = DynamicParamTable.getKeyIndex(entry.getKey());
|
||||
if (keyIndex == null) {
|
||||
// entry key is not present in DynamicParamTable, add it to EXTRA_PARAMS
|
||||
if (newExtraParams == null) {
|
||||
newExtraParams = new HashMap<>(EXTRA_PARAMS);
|
||||
}
|
||||
newExtraParams.put(entry.getKey(), entry.getValue());
|
||||
} else {
|
||||
if (newKey == null) {
|
||||
newKey = (BitSet) KEY.clone();
|
||||
}
|
||||
newKey.set(keyIndex);
|
||||
|
||||
if (parameters.size() > ADD_PARAMETER_ON_MOVE_THRESHOLD) {
|
||||
// recover VALUE back to Map
|
||||
if (newValueMap == null) {
|
||||
newValueMap = recoverCompressedValue();
|
||||
}
|
||||
newValueMap.put(keyIndex, DynamicParamTable.getValueIndex(entry.getKey(), entry.getValue()));
|
||||
} else if (!DynamicParamTable.isDefaultValue(entry.getKey(), entry.getValue())) {
|
||||
// add parameter by moving array
|
||||
newValueArray = addByMove(VALUE, KEY.get(0, keyIndex).cardinality(), DynamicParamTable.getValueIndex(entry.getKey(), entry.getValue()));
|
||||
} else {
|
||||
// value is default value, add to defaultKey directly
|
||||
if (defaultKey == null) {
|
||||
defaultKey = (BitSet) DEFAULT_KEY.clone();
|
||||
}
|
||||
defaultKey.set(keyIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newKey == null) {
|
||||
newKey = KEY;
|
||||
}
|
||||
if (defaultKey == null) {
|
||||
defaultKey = DEFAULT_KEY;
|
||||
}
|
||||
if (newValueArray == null && newValueMap == null) {
|
||||
newValueArray = VALUE;
|
||||
}
|
||||
if (newExtraParams == null) {
|
||||
newExtraParams = EXTRA_PARAMS;
|
||||
}
|
||||
if (newValueMap == null) {
|
||||
return new URLParam(newKey, defaultKey, newValueArray, newExtraParams, null);
|
||||
} else {
|
||||
return new URLParam(newKey, newValueMap, newExtraParams, null);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Integer, Integer> recoverCompressedValue() {
|
||||
Map<Integer, Integer> map = new HashMap<>((int) (KEY.size() / 0.75) + 1);
|
||||
for (int i = KEY.nextSetBit(0), offset = 0; i >= 0; i = KEY.nextSetBit(i + 1)) {
|
||||
if (!DEFAULT_KEY.get(i)) {
|
||||
map.put(i, VALUE[offset++]);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private int[] addByMove(int[] array, int index, int value) {
|
||||
if (index < 0 || index > array.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// copy-on-write
|
||||
int[] result = new int[array.length + 1];
|
||||
|
||||
System.arraycopy(array, 0, result, 0, index);
|
||||
result[index] = value;
|
||||
System.arraycopy(array, index, result, index + 1, array.length - index);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove specified parameters in URLParam
|
||||
*
|
||||
* @param keys keys to being removed
|
||||
* @return A new URLParam
|
||||
*/
|
||||
public URLParam removeParameters(String... keys) {
|
||||
if (keys == null || keys.length == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Map<String, String> map = new HashMap<>(getParameters());
|
||||
// lazy init, null if no modify
|
||||
BitSet newKey = null;
|
||||
BitSet defaultKey = null;
|
||||
int[] newValueArray = null;
|
||||
Map<String, String> newExtraParams = null;
|
||||
for (String key : keys) {
|
||||
map.remove(key);
|
||||
Integer keyIndex = DynamicParamTable.getKeyIndex(key);
|
||||
if (keyIndex != null && KEY.get(keyIndex)) {
|
||||
if (newKey == null) {
|
||||
newKey = (BitSet) KEY.clone();
|
||||
}
|
||||
newKey.clear(keyIndex);
|
||||
if (DEFAULT_KEY.get(keyIndex)) {
|
||||
// is default value, remove in DEFAULT_KEY
|
||||
if (defaultKey == null) {
|
||||
defaultKey = (BitSet) DEFAULT_KEY.clone();
|
||||
}
|
||||
defaultKey.clear(keyIndex);
|
||||
} else {
|
||||
// which offset is in VALUE array, set value as -1, compress in the end
|
||||
if (newValueArray == null) {
|
||||
newValueArray = new int[VALUE.length];
|
||||
System.arraycopy(VALUE, 0, newValueArray, 0, VALUE.length);
|
||||
}
|
||||
// KEY is immutable
|
||||
newValueArray[KEY.get(0, keyIndex).cardinality()] = -1;
|
||||
}
|
||||
}
|
||||
if (EXTRA_PARAMS.containsKey(key)) {
|
||||
if (newExtraParams == null) {
|
||||
newExtraParams = new HashMap<>(EXTRA_PARAMS);
|
||||
}
|
||||
newExtraParams.remove(key);
|
||||
}
|
||||
// ignore if key is absent
|
||||
}
|
||||
if (map.size() == getParameters().size()) {
|
||||
return this;
|
||||
if (newKey == null) {
|
||||
newKey = KEY;
|
||||
}
|
||||
if (defaultKey == null) {
|
||||
defaultKey = DEFAULT_KEY;
|
||||
}
|
||||
if (newValueArray == null) {
|
||||
newValueArray = VALUE;
|
||||
} else {
|
||||
// remove -1 value
|
||||
newValueArray = compressArray(newValueArray);
|
||||
}
|
||||
if (newExtraParams == null) {
|
||||
newExtraParams = EXTRA_PARAMS;
|
||||
}
|
||||
if (newKey.cardinality() + newExtraParams.size() == 0) {
|
||||
// empty, directly return cache
|
||||
return EMPTY_PARAM;
|
||||
} else {
|
||||
return new URLParam(newKey, defaultKey, newValueArray, newExtraParams, null);
|
||||
}
|
||||
return new URLParam(map, rawParam);
|
||||
}
|
||||
|
||||
private int[] compressArray(int[] array) {
|
||||
int total = 0;
|
||||
for (int i : array) {
|
||||
if (i > -1) {
|
||||
total++;
|
||||
}
|
||||
}
|
||||
if (total == 0) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
int[] result = new int[total];
|
||||
for (int i = 0, offset = 0; i < array.length; i++) {
|
||||
// skip if value if less than 0
|
||||
if (array[i] > -1) {
|
||||
result[offset++] = array[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all of the parameters in URLParam
|
||||
*
|
||||
* @return An empty URLParam
|
||||
*/
|
||||
public URLParam clearParameters() {
|
||||
return new URLParam(new HashMap<>());
|
||||
return EMPTY_PARAM;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if specified key is present in URLParam
|
||||
*
|
||||
* @param key specified key
|
||||
* @return present or not
|
||||
*/
|
||||
public boolean hasParameter(String key) {
|
||||
String value = getParameter(key);
|
||||
return value != null && value.length() > 0;
|
||||
Integer keyIndex = DynamicParamTable.getKeyIndex(key);
|
||||
if (keyIndex == null) {
|
||||
return EXTRA_PARAMS.containsKey(key);
|
||||
}
|
||||
return KEY.get(keyIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* get value of specified key in URLParam
|
||||
*
|
||||
* @param key specified key
|
||||
* @return value, null if key is absent
|
||||
*/
|
||||
public String getParameter(String key) {
|
||||
return params.get(key);
|
||||
Integer keyIndex = DynamicParamTable.getKeyIndex(key);
|
||||
if (keyIndex == null) {
|
||||
if (EXTRA_PARAMS.containsKey(key)) {
|
||||
return EXTRA_PARAMS.get(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (KEY.get(keyIndex)) {
|
||||
String value;
|
||||
if (DEFAULT_KEY.get(keyIndex)) {
|
||||
value = DynamicParamTable.getDefaultValue(keyIndex);
|
||||
} else {
|
||||
int offset = keyIndexToOffset(keyIndex);
|
||||
value = DynamicParamTable.getValue(keyIndex, offset);
|
||||
}
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
// Forward compatible, make sure key dynamic increment can work.
|
||||
// In that case, some values which are proceed before increment will set in EXTRA_PARAMS.
|
||||
return EXTRA_PARAMS.get(key);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int keyIndexToOffset(int keyIndex) {
|
||||
int arrayOffset = KEY.get(0, keyIndex).cardinality();
|
||||
return VALUE[arrayOffset];
|
||||
}
|
||||
|
||||
/**
|
||||
* get raw string like parameters
|
||||
*
|
||||
* @return raw string like parameters
|
||||
*/
|
||||
public String getRawParam() {
|
||||
return rawParam;
|
||||
if (StringUtils.isNotEmpty(rawParam)) {
|
||||
return rawParam;
|
||||
} else {
|
||||
// empty if parameters have been modified or init by Map
|
||||
return toString();
|
||||
}
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
|
|
@ -217,16 +712,23 @@ public class URLParam implements Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(params);
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
URLParam urlParam = (URLParam) o;
|
||||
return Objects.equals(KEY, urlParam.KEY)
|
||||
&& Objects.equals(DEFAULT_KEY, urlParam.DEFAULT_KEY)
|
||||
&& Arrays.equals(VALUE, urlParam.VALUE)
|
||||
&& Objects.equals(EXTRA_PARAMS, urlParam.EXTRA_PARAMS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof URLParam)) return false;
|
||||
URLParam that = (URLParam) obj;
|
||||
return Objects.equals(this.getParameters(), that.getParameters());
|
||||
public int hashCode() {
|
||||
return Objects.hash(KEY, DEFAULT_KEY, Arrays.hashCode(VALUE), EXTRA_PARAMS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -234,38 +736,71 @@ public class URLParam implements Serializable {
|
|||
if (StringUtils.isNotEmpty(rawParam)) {
|
||||
return rawParam;
|
||||
}
|
||||
if (params == null) {
|
||||
if ((KEY.cardinality() + EXTRA_PARAMS.size()) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, String> entry : new TreeMap<>(params).entrySet()) {
|
||||
if (StringUtils.isNotEmpty(entry.getKey())) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
buf.append("&");
|
||||
}
|
||||
buf.append(entry.getKey());
|
||||
buf.append("=");
|
||||
buf.append(entry.getValue() == null ? "" : entry.getValue().trim());
|
||||
}
|
||||
StringJoiner stringJoiner = new StringJoiner("&");
|
||||
for (int i = KEY.nextSetBit(0); i >= 0; i = KEY.nextSetBit(i + 1)) {
|
||||
String key = DynamicParamTable.getKey(i);
|
||||
String value = DEFAULT_KEY.get(i) ?
|
||||
DynamicParamTable.getDefaultValue(i) : DynamicParamTable.getValue(i, keyIndexToOffset(i));
|
||||
value = value == null ? "" : value.trim();
|
||||
stringJoiner.add(String.format("%s=%s", key, value));
|
||||
}
|
||||
return buf.toString();
|
||||
for (Map.Entry<String, String> entry : EXTRA_PARAMS.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String value = entry.getValue();
|
||||
value = value == null ? "" : value.trim();
|
||||
stringJoiner.add(String.format("%s=%s", key, value));
|
||||
}
|
||||
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URLParam
|
||||
* Init URLParam by constructor is not allowed
|
||||
* rawParam field in result will be null while {@link URLParam#getRawParam()} will automatically create it
|
||||
*
|
||||
* @param params params map added into URLParam
|
||||
* @return a new URLParam
|
||||
*/
|
||||
public static URLParam parse(Map<String, String> params) {
|
||||
return parse(params, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URLParam
|
||||
* Init URLParam by constructor is not allowed
|
||||
*
|
||||
* @param rawParam original rawParam string
|
||||
* @param encoded if parameters are URL encoded
|
||||
* @param extraParameters extra parameters to add into URLParam
|
||||
* @return a new URLParam
|
||||
*/
|
||||
public static URLParam parse(String rawParam, boolean encoded, Map<String, String> extraParameters) {
|
||||
Map<String, String> parameters = URLStrParser.parseParams(rawParam, encoded);
|
||||
if (CollectionUtils.isNotEmptyMap(extraParameters)) {
|
||||
parameters.putAll(extraParameters);
|
||||
}
|
||||
return new URLParam(parameters, rawParam);
|
||||
return parse(parameters, rawParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URLParam
|
||||
* Init URLParam by constructor is not allowed
|
||||
*
|
||||
* @param rawParam original rawParam string
|
||||
* @return a new URLParam
|
||||
*/
|
||||
public static URLParam parse(String rawParam) {
|
||||
String[] parts = rawParam.split("&");
|
||||
Map<String, String> parameters = new UnifiedMap<>((int) (parts.length/.75f) + 1);
|
||||
|
||||
BitSet keyBit = new BitSet((int) (parts.length / .75f) + 1);
|
||||
Map<Integer, Integer> valueMap = new HashMap<>((int) (parts.length / .75f) + 1);
|
||||
Map<String, String> extraParam = new HashMap<>((int) (parts.length / .75f) + 1);
|
||||
|
||||
for (String part : parts) {
|
||||
part = part.trim();
|
||||
if (part.length() > 0) {
|
||||
|
|
@ -273,16 +808,69 @@ public class URLParam implements Serializable {
|
|||
if (j >= 0) {
|
||||
String key = part.substring(0, j);
|
||||
String value = part.substring(j + 1);
|
||||
parameters.put(key, value);
|
||||
addParameter(keyBit, valueMap, extraParam, key, value, false);
|
||||
// compatible with lower versions registering "default." keys
|
||||
if (key.startsWith(DEFAULT_KEY_PREFIX)) {
|
||||
parameters.putIfAbsent(key.substring(DEFAULT_KEY_PREFIX.length()), value);
|
||||
addParameter(keyBit, valueMap, extraParam, key.substring(DEFAULT_KEY_PREFIX.length()), value, true);
|
||||
}
|
||||
} else {
|
||||
parameters.put(part, part);
|
||||
addParameter(keyBit, valueMap, extraParam, part, part, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new URLParam(parameters, rawParam);
|
||||
return new URLParam(keyBit, valueMap, extraParam, rawParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URLParam
|
||||
* Init URLParam by constructor is not allowed
|
||||
*
|
||||
* @param params params map added into URLParam
|
||||
* @param rawParam original rawParam string, directly add to rawParam field,
|
||||
* will not effect real key-pairs store in URLParam.
|
||||
* Please make sure it can correspond with params or will
|
||||
* cause unexpected result when calling {@link URLParam#getRawParam()}
|
||||
* and {@link URLParam#toString()} ()}. If you not sure, you can call
|
||||
* {@link URLParam#parse(String)} to init.
|
||||
* @return a new URLParam
|
||||
*/
|
||||
public static URLParam parse(Map<String, String> params, String rawParam) {
|
||||
if (CollectionUtils.isNotEmptyMap(params)) {
|
||||
BitSet keyBit = new BitSet((int) (params.size() / .75f) + 1);
|
||||
Map<Integer, Integer> valueMap = new HashMap<>((int) (params.size() / .75f) + 1);
|
||||
Map<String, String> extraParam = new HashMap<>((int) (params.size() / .75f) + 1);
|
||||
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
addParameter(keyBit, valueMap, extraParam, entry.getKey(), entry.getValue(), false);
|
||||
}
|
||||
return new URLParam(keyBit, valueMap, extraParam, rawParam);
|
||||
} else {
|
||||
return EMPTY_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addParameter(BitSet keyBit, Map<Integer, Integer> valueMap, Map<String, String> extraParam,
|
||||
String key, String value, boolean skipIfPresent) {
|
||||
Integer keyIndex = DynamicParamTable.getKeyIndex(key);
|
||||
if (skipIfPresent) {
|
||||
if (keyIndex == null) {
|
||||
if (extraParam.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (keyBit.get(keyIndex)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keyIndex == null) {
|
||||
extraParam.put(key, value);
|
||||
} else {
|
||||
if (!DynamicParamTable.isDefaultValue(key, value)) {
|
||||
valueMap.put(keyIndex, DynamicParamTable.getValueIndex(key, value));
|
||||
}
|
||||
keyBit.set(keyIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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.common.url.component.param;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Global Param Cache Table
|
||||
*/
|
||||
public final class DynamicParamTable {
|
||||
private static final List<String> KEYS = new CopyOnWriteArrayList<>();
|
||||
private static final List<ParamValue> VALUES = new CopyOnWriteArrayList<>();
|
||||
private static final Map<String, Integer> KEY2INDEX = new HashMap<>(64);
|
||||
|
||||
private DynamicParamTable() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
public static Integer getKeyIndex(String key) {
|
||||
return KEY2INDEX.get(key);
|
||||
}
|
||||
|
||||
public static int getValueIndex(String key, String value) {
|
||||
Integer idx = getKeyIndex(key);
|
||||
if (idx == null) {
|
||||
throw new IllegalArgumentException("Cannot found key in url param:" + key);
|
||||
}
|
||||
ParamValue paramValue = VALUES.get(idx);
|
||||
return paramValue.getIndex(value);
|
||||
}
|
||||
|
||||
public static String getKey(int offset) {
|
||||
return KEYS.get(offset);
|
||||
}
|
||||
|
||||
public static boolean isDefaultValue(String key, String value) {
|
||||
return Objects.equals(value, VALUES.get(getKeyIndex(key)).defaultVal());
|
||||
}
|
||||
|
||||
public static String getValue(int vi, int offset) {
|
||||
return VALUES.get(vi).getN(offset);
|
||||
}
|
||||
|
||||
public static String getDefaultValue(int vi) {
|
||||
return VALUES.get(vi).defaultVal();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
List<String> keys = new LinkedList<>();
|
||||
List<ParamValue> values = new LinkedList<>();
|
||||
Map<String, Integer> key2Index = new HashMap<>(64);
|
||||
keys.add("");
|
||||
values.add(new DynamicValues(null));
|
||||
|
||||
// Cache key and defaultValue
|
||||
keys.add("version");
|
||||
values.add(new DynamicValues(null));
|
||||
|
||||
keys.add("side");
|
||||
values.add(new FixedParamValue("consumer","provider"));
|
||||
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
if (!keys.get(i).isEmpty()) {
|
||||
key2Index.put(keys.get(i), i);
|
||||
}
|
||||
}
|
||||
|
||||
KEYS.addAll(keys);
|
||||
VALUES.addAll(values);
|
||||
KEY2INDEX.putAll(key2Index);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.common.url.component.param;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class DynamicValues implements ParamValue {
|
||||
private final Map<Integer, String> index2Value = new ConcurrentHashMap<>();
|
||||
private final Map<String, Integer> value2Index = new ConcurrentHashMap<>();
|
||||
private int indexSeq = 0;
|
||||
|
||||
public DynamicValues(String defaultVal) {
|
||||
if (defaultVal == null) {
|
||||
indexSeq += 1;
|
||||
} else {
|
||||
add(defaultVal);
|
||||
}
|
||||
}
|
||||
|
||||
public int add(String value) {
|
||||
Integer index = value2Index.get(value);
|
||||
if (index != null) {
|
||||
return index;
|
||||
} else {
|
||||
synchronized (this) {
|
||||
// thread safe
|
||||
if (!value2Index.containsKey(value)) {
|
||||
value2Index.put(value, indexSeq);
|
||||
index2Value.put(indexSeq, value);
|
||||
indexSeq += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value2Index.get(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getN(int n) {
|
||||
return index2Value.get(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex(String value) {
|
||||
Integer index = value2Index.get(value);
|
||||
if (index == null) {
|
||||
return add(value);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultVal() {
|
||||
return getN(0);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.common.url.component.param;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* In lower case
|
||||
*/
|
||||
public class FixedParamValue implements ParamValue {
|
||||
private final String[] values;
|
||||
private final Map<String, Integer> val2Index;
|
||||
|
||||
public FixedParamValue(String... values) {
|
||||
if (values.length == 0) {
|
||||
throw new IllegalArgumentException("the array size of values should be larger than 0");
|
||||
}
|
||||
this.values = values;
|
||||
Map<String, Integer> valueMap = new HashMap<>(values.length);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i] != null) {
|
||||
valueMap.put(values[i].toLowerCase(Locale.ROOT), i);
|
||||
}
|
||||
}
|
||||
val2Index = Collections.unmodifiableMap(valueMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* DEFAULT value will be returned if n = 0
|
||||
*/
|
||||
@Override
|
||||
public String getN(int n) {
|
||||
return values[n];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndex(String value) {
|
||||
Integer offset = val2Index.get(value.toLowerCase(Locale.ROOT));
|
||||
if (offset == null) {
|
||||
throw new IllegalArgumentException("unrecognized value " + value
|
||||
+ " , please check if value is illegal. " +
|
||||
"Permitted values: " + Arrays.asList(values));
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultVal() {
|
||||
return values[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.common.url.component.param;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class IgnoredParam {
|
||||
private static final HashSet<String> IGNORED = new HashSet<>();
|
||||
|
||||
static {
|
||||
IGNORED.add("timestamp");
|
||||
}
|
||||
|
||||
static boolean ignore(String key) {
|
||||
return IGNORED.contains(key);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.common.url.component.param;
|
||||
|
||||
public interface ParamValue {
|
||||
/**
|
||||
* get value at the specified index.
|
||||
*
|
||||
* @param n the nth value
|
||||
* @return the value stored at index = n
|
||||
*/
|
||||
String getN(int n);
|
||||
|
||||
|
||||
/**
|
||||
* max index is 2^31 - 1
|
||||
*
|
||||
* @param value the stored value
|
||||
* @return the index of value
|
||||
*/
|
||||
int getIndex(String value);
|
||||
|
||||
/**
|
||||
* get default value
|
||||
*
|
||||
* @return the default value stored at index = 0
|
||||
*/
|
||||
String defaultVal();
|
||||
}
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* 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.common.url;
|
||||
|
||||
import org.apache.dubbo.common.url.component.URLParam;
|
||||
import org.apache.dubbo.common.utils.CollectionUtils;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class URLParamTest {
|
||||
@Test
|
||||
public void testParseWithRawParam() {
|
||||
URLParam urlParam1 = URLParam.parse("aaa=aaa&bbb&version=1.0&default.ccc=123");
|
||||
Assertions.assertEquals("aaa", urlParam1.getParameter("aaa"));
|
||||
Assertions.assertEquals("bbb", urlParam1.getParameter("bbb"));
|
||||
Assertions.assertEquals("1.0", urlParam1.getParameter("version"));
|
||||
Assertions.assertEquals("123", urlParam1.getParameter("default.ccc"));
|
||||
Assertions.assertEquals("123", urlParam1.getParameter("ccc"));
|
||||
Assertions.assertEquals(urlParam1, URLParam.parse(urlParam1.getRawParam()));
|
||||
Assertions.assertEquals(urlParam1, URLParam.parse(urlParam1.toString()));
|
||||
|
||||
URLParam urlParam2 = URLParam.parse("aaa%3dtest", true, null);
|
||||
Assertions.assertEquals("test", urlParam2.getParameter("aaa"));
|
||||
|
||||
Map<String, String> overrideMap = Collections.singletonMap("aaa", "bbb");
|
||||
URLParam urlParam3 = URLParam.parse("aaa%3dtest", true, overrideMap);
|
||||
Assertions.assertEquals("bbb", urlParam3.getParameter("aaa"));
|
||||
|
||||
URLParam urlParam4 = URLParam.parse("ccc=456&&default.ccc=123");
|
||||
Assertions.assertEquals("456", urlParam4.getParameter("ccc"));
|
||||
|
||||
URLParam urlParam5 = URLParam.parse("version=2.0&&default.version=1.0");
|
||||
Assertions.assertEquals("2.0", urlParam5.getParameter("version"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseWithMap() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("aaa", "aaa");
|
||||
map.put("bbb", "bbb");
|
||||
map.put("version", "2.0");
|
||||
map.put("side", "consumer");
|
||||
|
||||
URLParam urlParam1 = URLParam.parse(map);
|
||||
Assertions.assertEquals("aaa", urlParam1.getParameter("aaa"));
|
||||
Assertions.assertEquals("bbb", urlParam1.getParameter("bbb"));
|
||||
Assertions.assertEquals("2.0", urlParam1.getParameter("version"));
|
||||
Assertions.assertEquals("consumer", urlParam1.getParameter("side"));
|
||||
Assertions.assertEquals(urlParam1, URLParam.parse(urlParam1.getRawParam()));
|
||||
|
||||
map.put("bbb", "ccc");
|
||||
|
||||
Assertions.assertEquals("bbb", urlParam1.getParameter("bbb"));
|
||||
|
||||
URLParam urlParam2 = URLParam.parse(map);
|
||||
Assertions.assertEquals("ccc", urlParam2.getParameter("bbb"));
|
||||
|
||||
URLParam urlParam3 = URLParam.parse(null, null);
|
||||
Assertions.assertFalse(urlParam3.hasParameter("aaa"));
|
||||
Assertions.assertEquals(urlParam3, URLParam.parse(urlParam3.getRawParam()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParameter() {
|
||||
URLParam urlParam1 = URLParam.parse("aaa=aaa&bbb&version=1.0&default.ccc=123");
|
||||
Assertions.assertNull(urlParam1.getParameter("abcde"));
|
||||
|
||||
URLParam urlParam2 = URLParam.parse("aaa=aaa&bbb&default.ccc=123");
|
||||
Assertions.assertNull(urlParam2.getParameter("version"));
|
||||
|
||||
URLParam urlParam3 = URLParam.parse("aaa=aaa&side=consumer");
|
||||
Assertions.assertEquals("consumer", urlParam3.getParameter("side"));
|
||||
|
||||
URLParam urlParam4 = URLParam.parse("aaa=aaa&side=provider");
|
||||
Assertions.assertEquals("provider", urlParam4.getParameter("side"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameter() {
|
||||
URLParam urlParam1 = URLParam.parse("aaa=aaa&side=provider");
|
||||
Assertions.assertTrue(urlParam1.hasParameter("aaa"));
|
||||
Assertions.assertFalse(urlParam1.hasParameter("bbb"));
|
||||
Assertions.assertTrue(urlParam1.hasParameter("side"));
|
||||
Assertions.assertFalse(urlParam1.hasParameter("version"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveParameters() {
|
||||
URLParam urlParam1 = URLParam.parse("aaa=aaa&side=provider&version=1.0");
|
||||
Assertions.assertTrue(urlParam1.hasParameter("aaa"));
|
||||
Assertions.assertTrue(urlParam1.hasParameter("side"));
|
||||
Assertions.assertTrue(urlParam1.hasParameter("version"));
|
||||
|
||||
URLParam urlParam2 = urlParam1.removeParameters("side");
|
||||
Assertions.assertFalse(urlParam2.hasParameter("side"));
|
||||
|
||||
URLParam urlParam3 = urlParam1.removeParameters("aaa", "version");
|
||||
Assertions.assertFalse(urlParam3.hasParameter("aaa"));
|
||||
Assertions.assertFalse(urlParam3.hasParameter("version"));
|
||||
|
||||
URLParam urlParam4 = urlParam1.removeParameters();
|
||||
Assertions.assertTrue(urlParam4.hasParameter("aaa"));
|
||||
Assertions.assertTrue(urlParam4.hasParameter("side"));
|
||||
Assertions.assertTrue(urlParam4.hasParameter("version"));
|
||||
|
||||
URLParam urlParam5 = urlParam1.clearParameters();
|
||||
Assertions.assertFalse(urlParam5.hasParameter("aaa"));
|
||||
Assertions.assertFalse(urlParam5.hasParameter("side"));
|
||||
Assertions.assertFalse(urlParam5.hasParameter("version"));
|
||||
|
||||
URLParam urlParam6 = urlParam1.removeParameters("aaa");
|
||||
Assertions.assertFalse(urlParam6.hasParameter("aaa"));
|
||||
|
||||
URLParam urlParam7 = URLParam.parse("side=consumer").removeParameters("side");
|
||||
Assertions.assertFalse(urlParam7.hasParameter("side"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddParameters() {
|
||||
URLParam urlParam1 = URLParam.parse("aaa=aaa&side=provider");
|
||||
Assertions.assertTrue(urlParam1.hasParameter("aaa"));
|
||||
Assertions.assertTrue(urlParam1.hasParameter("side"));
|
||||
|
||||
URLParam urlParam2 = urlParam1.addParameter("bbb", "bbb");
|
||||
Assertions.assertEquals("aaa", urlParam2.getParameter("aaa"));
|
||||
Assertions.assertEquals("bbb", urlParam2.getParameter("bbb"));
|
||||
|
||||
URLParam urlParam3 = urlParam1.addParameter("aaa", "ccc");
|
||||
Assertions.assertEquals("aaa", urlParam1.getParameter("aaa"));
|
||||
Assertions.assertEquals("ccc", urlParam3.getParameter("aaa"));
|
||||
|
||||
URLParam urlParam4 = urlParam1.addParameter("aaa", "aaa");
|
||||
Assertions.assertEquals("aaa", urlParam4.getParameter("aaa"));
|
||||
|
||||
URLParam urlParam5 = urlParam1.addParameter("version", "0.1");
|
||||
Assertions.assertEquals("0.1", urlParam5.getParameter("version"));
|
||||
|
||||
URLParam urlParam6 = urlParam5.addParameterIfAbsent("version", "0.2");
|
||||
Assertions.assertEquals("0.1", urlParam6.getParameter("version"));
|
||||
|
||||
URLParam urlParam7 = urlParam1.addParameterIfAbsent("version", "0.2");
|
||||
Assertions.assertEquals("0.2", urlParam7.getParameter("version"));
|
||||
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("version", "1.0");
|
||||
map.put("side", "provider");
|
||||
|
||||
URLParam urlParam8 = urlParam1.addParameters(map);
|
||||
Assertions.assertEquals("1.0", urlParam8.getParameter("version"));
|
||||
Assertions.assertEquals("provider", urlParam8.getParameter("side"));
|
||||
|
||||
map.put("side", "consumer");
|
||||
|
||||
Assertions.assertEquals("provider", urlParam8.getParameter("side"));
|
||||
|
||||
URLParam urlParam9 = urlParam8.addParameters(map);
|
||||
Assertions.assertEquals("consumer", urlParam9.getParameter("side"));
|
||||
|
||||
URLParam urlParam10 = urlParam8.addParametersIfAbsent(map);
|
||||
Assertions.assertEquals("provider", urlParam10.getParameter("side"));
|
||||
|
||||
Assertions.assertThrows(IllegalArgumentException.class,
|
||||
() -> urlParam1.addParameter("side", "unrecognized"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testURLParamMap() {
|
||||
URLParam urlParam1 = URLParam.parse("");
|
||||
Assertions.assertTrue(urlParam1.getParameters().isEmpty());
|
||||
Assertions.assertEquals(0, urlParam1.getParameters().size());
|
||||
Assertions.assertFalse(urlParam1.getParameters().containsKey("aaa"));
|
||||
Assertions.assertFalse(urlParam1.getParameters().containsKey("version"));
|
||||
Assertions.assertFalse(urlParam1.getParameters().containsKey(new Object()));
|
||||
|
||||
URLParam urlParam2 = URLParam.parse("aaa=aaa&version=1.0");
|
||||
URLParam.URLParamMap urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters();
|
||||
Assertions.assertTrue(urlParam2Map.containsKey("version"));
|
||||
Assertions.assertFalse(urlParam2Map.containsKey("side"));
|
||||
|
||||
Assertions.assertTrue(urlParam2Map.containsValue("1.0"));
|
||||
Assertions.assertFalse(urlParam2Map.containsValue("2.0"));
|
||||
|
||||
Assertions.assertEquals("1.0", urlParam2Map.get("version"));
|
||||
Assertions.assertEquals("aaa", urlParam2Map.get("aaa"));
|
||||
Assertions.assertNull(urlParam2Map.get(new Object()));
|
||||
|
||||
Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
urlParam2Map.put("version", "1.0");
|
||||
Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
urlParam2Map.putAll(Collections.singletonMap("version", "1.0"));
|
||||
Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
urlParam2Map.put("side", "consumer");
|
||||
Assertions.assertNotEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters();
|
||||
Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
urlParam2Map.remove("version");
|
||||
Assertions.assertNotEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
Assertions.assertFalse(urlParam2Map.containsValue("version"));
|
||||
Assertions.assertNull(urlParam2Map.getUrlParam().getParameter("version"));
|
||||
|
||||
urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters();
|
||||
Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
urlParam2Map.clear();
|
||||
Assertions.assertTrue(urlParam2Map.isEmpty());
|
||||
Assertions.assertEquals(0, urlParam2Map.size());
|
||||
Assertions.assertNull(urlParam2Map.getUrlParam().getParameter("aaa"));
|
||||
Assertions.assertNull(urlParam2Map.getUrlParam().getParameter("version"));
|
||||
|
||||
urlParam2Map = (URLParam.URLParamMap) urlParam2.getParameters();
|
||||
Assertions.assertEquals(urlParam2, urlParam2Map.getUrlParam());
|
||||
|
||||
URLParam urlParam3 = URLParam.parse("aaa=aaa&version=1.0");
|
||||
Assertions.assertTrue(CollectionUtils.mapEquals(urlParam2Map, urlParam3.getParameters()));
|
||||
Assertions.assertTrue(CollectionUtils.equals(urlParam2Map.entrySet(), urlParam3.getParameters().entrySet()));
|
||||
Assertions.assertTrue(CollectionUtils.equals(urlParam2Map.keySet(), urlParam3.getParameters().keySet()));
|
||||
Assertions.assertTrue(CollectionUtils.equals(urlParam2Map.values(), urlParam3.getParameters().values()));
|
||||
|
||||
URLParam urlParam4 = URLParam.parse("aaa=aaa&version=1.0&side=consumer");
|
||||
Assertions.assertFalse(CollectionUtils.mapEquals(urlParam2Map, urlParam4.getParameters()));
|
||||
Assertions.assertFalse(CollectionUtils.equals(urlParam2Map.entrySet(), urlParam4.getParameters().entrySet()));
|
||||
Assertions.assertFalse(CollectionUtils.equals(urlParam2Map.keySet(), urlParam4.getParameters().keySet()));
|
||||
Assertions.assertFalse(CollectionUtils.equals(urlParam2Map.values(), urlParam4.getParameters().values()));
|
||||
|
||||
Set<Map<String,String>> set = new HashSet<>();
|
||||
|
||||
set.add(urlParam2Map);
|
||||
set.add(urlParam3.getParameters());
|
||||
Assertions.assertEquals(1,set.size());
|
||||
|
||||
set.add(urlParam4.getParameters());
|
||||
Assertions.assertEquals(2,set.size());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue