com.datastax.dse.driver.internal.core.insights.PlatformInfoFinder Maven / Gradle / Ivy
/*
* 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 com.datastax.dse.driver.internal.core.insights;
import static com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo.OS;
import static com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo.RuntimeAndCompileTimeVersions;
import com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo;
import com.datastax.dse.driver.internal.core.insights.schema.InsightsPlatformInfo.CPUS;
import com.datastax.oss.driver.internal.core.os.Native;
import com.datastax.oss.driver.shaded.guava.common.annotations.VisibleForTesting;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Function;
import java.util.regex.Pattern;
class PlatformInfoFinder {
private static final String MAVEN_IGNORE_LINE = "The following files have been resolved:";
private static final Pattern DEPENDENCY_SPLIT_REGEX = Pattern.compile(":");
static final String UNVERIFIED_RUNTIME_VERSION = "UNVERIFIED";
private final Function propertiesUrlProvider;
@SuppressWarnings("UnnecessaryLambda")
private static final Function M2_PROPERTIES_PROVIDER =
d -> {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader == null) {
contextClassLoader = PlatformInfoFinder.class.getClassLoader();
}
return contextClassLoader.getResource(
"META-INF/maven/" + d.groupId + "/" + d.artifactId + "/pom.properties");
};
PlatformInfoFinder() {
this(M2_PROPERTIES_PROVIDER);
}
@VisibleForTesting
PlatformInfoFinder(Function pomPropertiesUrlProvider) {
this.propertiesUrlProvider = pomPropertiesUrlProvider;
}
InsightsPlatformInfo getInsightsPlatformInfo() {
OS os = getOsInfo();
CPUS cpus = getCpuInfo();
Map> runtimeInfo = getRuntimeInfo();
return new InsightsPlatformInfo(os, cpus, runtimeInfo);
}
private Map> getRuntimeInfo() {
Map coreDeps =
fetchDependenciesFromFile(
this.getClass().getResourceAsStream("/com/datastax/dse/driver/internal/deps.txt"));
Map queryBuilderDeps =
fetchDependenciesFromFile(
this.getClass()
.getResourceAsStream("/com/datastax/dse/driver/internal/querybuilder/deps.txt"));
Map mapperProcessorDeps =
fetchDependenciesFromFile(
this.getClass()
.getResourceAsStream(
"/com/datastax/dse/driver/internal/mapper/processor/deps.txt"));
Map mapperRuntimeDeps =
fetchDependenciesFromFile(
this.getClass()
.getResourceAsStream("/com/datastax/dse/driver/internal/mapper/deps.txt"));
Map> runtimeDependencies =
new LinkedHashMap<>();
putIfNonEmpty(coreDeps, runtimeDependencies, "core");
putIfNonEmpty(queryBuilderDeps, runtimeDependencies, "query-builder");
putIfNonEmpty(mapperProcessorDeps, runtimeDependencies, "mapper-processor");
putIfNonEmpty(mapperRuntimeDeps, runtimeDependencies, "mapper-runtime");
addJavaVersion(runtimeDependencies);
return runtimeDependencies;
}
private void putIfNonEmpty(
Map moduleDependencies,
Map> runtimeDependencies,
String moduleName) {
if (!moduleDependencies.isEmpty()) {
runtimeDependencies.put(moduleName, moduleDependencies);
}
}
@VisibleForTesting
void addJavaVersion(Map> runtimeDependencies) {
Package javaPackage = Runtime.class.getPackage();
Map javaDependencies = new LinkedHashMap<>();
javaDependencies.put(
"version", toSameRuntimeAndCompileVersion(javaPackage.getImplementationVersion()));
javaDependencies.put(
"vendor", toSameRuntimeAndCompileVersion(javaPackage.getImplementationVendor()));
javaDependencies.put(
"title", toSameRuntimeAndCompileVersion(javaPackage.getImplementationTitle()));
putIfNonEmpty(javaDependencies, runtimeDependencies, "java");
}
private RuntimeAndCompileTimeVersions toSameRuntimeAndCompileVersion(String version) {
return new RuntimeAndCompileTimeVersions(version, version, false);
}
/**
* Method is fetching dependencies from file. Lines in file should be in format:
* com.organization:artifactId:jar:1.2.0 or com.organization:artifactId:jar:native:1.2.0
*
* For such file the output will be: Map
* "com.organization:artifactId",{"runtimeVersion":"1.2.0", "compileVersion:"1.2.0", "optional":
* false} Duplicates will be omitted. If there are two dependencies for the exactly the same
* organizationId:artifactId it is not deterministic which version will be taken. In the case of
* an error while opening file this method will fail silently returning an empty Map
*/
@VisibleForTesting
Map fetchDependenciesFromFile(InputStream inputStream) {
Map dependencies = new LinkedHashMap<>();
if (inputStream == null) {
return dependencies;
}
try {
List dependenciesFromFile = extractMavenDependenciesFromFile(inputStream);
for (DependencyFromFile d : dependenciesFromFile) {
dependencies.put(formatDependencyName(d), getRuntimeAndCompileVersion(d));
}
} catch (IOException e) {
return dependencies;
}
return dependencies;
}
private RuntimeAndCompileTimeVersions getRuntimeAndCompileVersion(DependencyFromFile d) {
URL url = propertiesUrlProvider.apply(d);
if (url == null) {
return new RuntimeAndCompileTimeVersions(
UNVERIFIED_RUNTIME_VERSION, d.getVersion(), d.isOptional());
}
Properties properties = new Properties();
try {
properties.load(url.openStream());
} catch (IOException e) {
return new RuntimeAndCompileTimeVersions(
UNVERIFIED_RUNTIME_VERSION, d.getVersion(), d.isOptional());
}
Object version = properties.get("version");
if (version == null) {
return new RuntimeAndCompileTimeVersions(
UNVERIFIED_RUNTIME_VERSION, d.getVersion(), d.isOptional());
} else {
return new RuntimeAndCompileTimeVersions(version.toString(), d.getVersion(), d.isOptional());
}
}
private String formatDependencyName(DependencyFromFile d) {
return String.format("%s:%s", d.getGroupId(), d.getArtifactId());
}
private List extractMavenDependenciesFromFile(InputStream inputStream)
throws IOException {
List dependenciesFromFile = new ArrayList<>();
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
for (String line; (line = reader.readLine()) != null; ) {
if (lineWithDependencyInfo(line)) {
dependenciesFromFile.add(extractDependencyFromLine(line.trim()));
}
}
return dependenciesFromFile;
}
private DependencyFromFile extractDependencyFromLine(String line) {
String[] split = DEPENDENCY_SPLIT_REGEX.split(line);
if (split.length == 6) { // case for i.e.: com.github.jnr:jffi:jar:native:1.2.16:compile
return new DependencyFromFile(split[0], split[1], split[4], checkIsOptional(split[5]));
} else { // case for normal: org.ow2.asm:asm:jar:5.0.3:compile
return new DependencyFromFile(split[0], split[1], split[3], checkIsOptional(split[4]));
}
}
private boolean checkIsOptional(String scope) {
return scope.contains("(optional)");
}
private boolean lineWithDependencyInfo(String line) {
return (!line.equals(MAVEN_IGNORE_LINE) && !line.isEmpty());
}
private CPUS getCpuInfo() {
int numberOfProcessors = Runtime.getRuntime().availableProcessors();
String model = Native.getCpu();
return new CPUS(numberOfProcessors, model);
}
private OS getOsInfo() {
String osName = System.getProperty("os.name");
String osVersion = System.getProperty("os.version");
String osArch = System.getProperty("os.arch");
return new OS(osName, osVersion, osArch);
}
static class DependencyFromFile {
private final String groupId;
private final String artifactId;
private final String version;
private final boolean optional;
DependencyFromFile(String groupId, String artifactId, String version, boolean optional) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.optional = optional;
}
String getGroupId() {
return groupId;
}
String getArtifactId() {
return artifactId;
}
String getVersion() {
return version;
}
boolean isOptional() {
return optional;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DependencyFromFile)) {
return false;
}
DependencyFromFile that = (DependencyFromFile) o;
return optional == that.optional
&& Objects.equals(groupId, that.groupId)
&& Objects.equals(artifactId, that.artifactId)
&& Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(groupId, artifactId, version, optional);
}
@Override
public String toString() {
return "DependencyFromFile{"
+ "groupId='"
+ groupId
+ '\''
+ ", artifactId='"
+ artifactId
+ '\''
+ ", version='"
+ version
+ '\''
+ ", optional="
+ optional
+ '}';
}
}
}