org.opendaylight.jsonrpc.bus.messagelib.Util Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2017 Brocade Communications Systems, Inc. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.jsonrpc.bus.messagelib;
import static org.opendaylight.jsonrpc.bus.jsonrpc.JsonRpcConstants.canRepresentJsonPrimitive;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Joiner.MapJoiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ComparisonChain;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.opendaylight.jsonrpc.bus.jsonrpc.JsonRpcBaseRequestMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Helper/utility class. Some of methods provided by this utility class are not
* meant to be used directly, or just during tests.
*
* @author Richard Kosegi
*/
public final class Util {
private static final Logger LOG = LoggerFactory.getLogger(Util.class);
private static final MapJoiner QUERY_JOINER = Joiner.on('&').withKeyValueSeparator("=");
private Util() {
// noop
}
/**
* Removes given query parameters from URI query string. If array of
* parameter names to remove is empty, original query string is returned
* instead.
*
* @param rawQuery query string from URI
* @param paramsToRemove array of parameter names to remove
* @return query string with removed parameters
*/
public static String removeParams(String rawQuery, String... paramsToRemove) {
// nothing to remove
if (paramsToRemove == null || paramsToRemove.length == 0) {
return rawQuery;
}
final Map params = tokenizeQuery(rawQuery);
Map map = params.entrySet()
.stream()
.filter(e -> !Arrays.asList(paramsToRemove).contains(e.getKey().trim()))
// collect into LinkedHashMap, which preserves insertion order
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (first, second) -> first,
LinkedHashMap::new));
return QUERY_JOINER.join(map);
}
/**
* Originally I used Guava's {@link Splitter} and {@link Joiner} to
* manipulate URI, but they don't like fact, that URI query parameter does
* not require value, which is perfectly fine with RFC-2396.
*/
@VisibleForTesting
static final class UriTokenizer {
private static final Splitter PARAM_SPLITTER = Splitter.on("&");
private UriTokenizer() {
// no instantiation here
}
/**
* Parse query parameters into key-value mapping.
*
* @param queryParams query parameters from URI
* @return key-value mapping of query parameters
*/
public static Map tokenize(String queryParams) {
final Map ret = new LinkedHashMap<>();
final Iterable paramTokens = PARAM_SPLITTER.split(queryParams != null ? queryParams : "");
for (final String tok : paramTokens) {
String[] parts = tok.split("=");
if (!"".equals(parts[0])) {
ret.put(parts[0], parts.length == 2 ? parts[1] : null);
}
}
LOG.trace("Tokenized : {} into {}", queryParams, ret);
return ret;
}
}
static Map tokenizeQuery(String rawUri) {
return UriTokenizer.tokenize(rawUri);
}
public static int getParametersCount(final JsonRpcBaseRequestMessage msg) {
if (msg.getParams() instanceof JsonArray) {
return ((JsonArray) msg.getParams()).size();
}
if (msg.getParams() instanceof JsonPrimitive) {
return 1;
}
if (msg.getParams() instanceof JsonObject) {
return 1;
}
return 0;
}
@SuppressWarnings("checkstyle:IllegalCatch")
@SuppressFBWarnings("DE_MIGHT_IGNORE")
public static void closeQuietly(AutoCloseable autoCloseable) {
try {
autoCloseable.close();
} catch (Exception e) {
// NOOP
}
}
/**
* Sorts list of matched methods based on preference. Currently it only
* prefers method without underscore in it's name
*
* @return {@link Comparator}
*/
public static Comparator nameSorter() {
return (o1, o2) -> {
if (o1.getName().contains("_")) {
return 1;
}
if (o2.getName().contains("_")) {
return -1;
}
return o1.getName().compareTo(o2.getName());
};
}
/**
* In order to have deterministic order of methods, we need to sort them by
* argument types. This is because outcome of
* {@link Class#getDeclaredMethods()} is not sorted.
* @return {@link Comparator}
*/
public static Comparator argsSorter() {
return (left,
right) -> Arrays.asList(left.getParameterTypes())
.stream()
.map(Object::toString)
.collect(Collectors.toList())
.hashCode()
- Arrays.asList(right.getParameterTypes())
.stream()
.map(Object::toString)
.collect(Collectors.toList())
.hashCode();
}
/**
* Combination of {@link #nameSorter()} and {@link #argsSorter()}.
* @return combined {@link Comparator}
*/
public static Comparator nameAndArgsSorter() {
return (left, right) -> ComparisonChain.start()
.compare(left, right, argsSorter())
.compare(left, right, nameSorter())
.result();
}
/**
* Parse query parameter value from URI or provide default value if not present.
*
* @param uri endpoint URI to parse value from
* @param queryParamName query parameter name
* @param defaultValue default value to use if not present
* @return query parameter value
*/
public static long queryParamValue(String uri, String queryParamName, long defaultValue) {
try {
final URI parsed = new URI(uri);
return Long.parseLong(tokenizeQuery(parsed.getQuery()).computeIfAbsent(queryParamName,
t -> String.valueOf(defaultValue)));
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Inject query parameter with value into URI, if it not exists. Multi-valued query parameters are not supported.
*
* @param uri URI to inject parameter into
* @param queryParamName query parameter name to inject
* @param queryParamValue query parameter value
* @return modified URI
*/
public static String injectQueryParam(String uri, String queryParamName, String queryParamValue) {
try {
final URI parsed = new URI(uri);
final Map params = tokenizeQuery(parsed.getQuery());
params.putIfAbsent(queryParamName, queryParamValue);
final StringBuilder sb = new StringBuilder();
sb.append(parsed.getScheme()).append("://").append(parsed.getHost());
if (parsed.getPort() != -1) {
sb.append(':').append(parsed.getPort());
}
if (parsed.getPath() != null) {
sb.append(parsed.getPath());
}
sb.append('?').append(QUERY_JOINER.join(params));
return sb.toString();
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Comparator that sorts {@link Method} according to payload type. It considers parameter type and {@link Method}
* argument type. It is assumed that comparison is done on filtered method already (where method name and number of
* arguments matched parameter count in payload).
*
* @param params message payload parameter
* @return {@link Comparator}
*/
public static Comparator payloadAwareSorter(JsonElement params) {
return (left, right) -> {
if (params == null || params.isJsonNull() || left.getParameterCount() == 0) {
return 0;
}
if (params.isJsonPrimitive() && canRepresentJsonPrimitive(right.getParameterTypes()[0])) {
return 1;
}
if (params.isJsonPrimitive() && canRepresentJsonPrimitive(left.getParameterTypes()[0])) {
return -1;
}
if (params.isJsonObject() && !canRepresentJsonPrimitive(right.getParameterTypes()[0])) {
return 1;
}
if (params.isJsonObject() && !canRepresentJsonPrimitive(left.getParameterTypes()[0])) {
return -1;
}
return 0;
};
}
/**
* Await for underlying transport o become ready. This is needed when request is made, but transport not yet
* finished handshake.
*
* @param session {@link ClientSession} to await for
* @param milliseconds period to wait for (at most)
*/
static void awaitForTransport(ClientSession session, long milliseconds) {
final long future = System.currentTimeMillis() + milliseconds;
while (System.currentTimeMillis() < future) {
if (session.isConnectionReady()) {
return;
}
Uninterruptibles.sleepUninterruptibly(100L, TimeUnit.MILLISECONDS);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy