8000 JAVA-1418: Make Guava version detection more reliable (#825) · kecmu/java-driver@109c529 · GitHub
[go: up one dir, main page]

Skip to content

Commit 109c529

Browse files
authored
JAVA-1418: Make Guava version detection more reliable (apache#825)
1 parent edc682d commit 109c529

File tree

2 files changed

+50
-63
lines changed

2 files changed

+50
-63
lines changed

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [improvement] JAVA-1247: Disable idempotence warnings.
1717
- [improvement] JAVA-1286: Support setting and retrieving udt fields in QueryBuilder.
1818
- [bug] JAVA-1415: Correctly report if a UDT column is frozen.
19+
- [bug] JAVA-1418: Make Guava version detection more reliable.
1920

2021
Merged from 3.1.x branch:
2122

driver-core/src/main/java/com/datastax/driver/core/GuavaCompatibility.java

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,17 @@
1616
package com.datastax.driver.core;
1717

1818
import com.datastax.driver.core.exceptions.DriverInternalError;
19-
import com.google.common.base.Preconditions;
19+
import com.google.common.collect.BiMap;
20+
import com.google.common.collect.Maps;
2021
import com.google.common.reflect.TypeToken;
2122
import com.google.common.util.concurrent.*;
2223
import org.slf4j.Logger;
2324
import org.slf4j.LoggerFactory;
2425

25-
import java.io.IOException;
26-
import java.io.InputStream;
26+
import java.lang.reflect.ParameterizedType;
2727
import java.lang.reflect.Type;
28-
import java.net.URL;
29-
import java.util.Enumeration;
28+
import java.util.Map;
3029
import java.util.concurrent.Executor;
31-
import java.util.jar.Attributes;
32-
import java.util.jar.Manifest;
33-
import java.util.regex.Matcher;
34-
import java.util.regex.Pattern;
3530

3631
/**
3732
* A compatibility layer to support a wide range of Guava versions.
@@ -50,7 +45,6 @@
5045
public abstract class GuavaCompatibility {
5146

5247
private static final Logger logger = LoggerFactory.getLogger(GuavaCompatibility.class);
53-
private static final Pattern GA_VERSION_EXTRACTOR = Pattern.compile("(\\d+\\.\\d+\\.\\d+).*");
5448

5549
/**
5650
* The unique instance of this class, that is compatible with the Guava version found in the classpath.
@@ -134,61 +128,15 @@ public abstract <I, O> ListenableFuture<O> transformAsync(ListenableFuture<I> in
134128
public abstract Executor sameThreadExecutor();
135129

136130
private static GuavaCompatibility selectImplementation() {
137-
String fullVersion = getBundleVersion(loadGuavaManifest());
138-
// Get rid of potential rc qualifier, as it could throw off the lexical comparisons
139-
String version = stripQualifiers(fullVersion);
140-
if (version.compareTo("16.0.1") < 0) {
141-
throw new DriverInternalError(String.format(
142-
"Detected incompatible version of Guava in the classpath (%s). " +
143-
"You need 16.0.1 or higher.", fullVersion));
144-
} else if (version.compareTo("19.0") < 0) {
145-
logger.info("Detected Guava {} in the classpath, using pre-19 compatibility layer", fullVersion);
146-
return new Version18OrLower();
147-
} else {
148-
logger.info("Detected Guava {} in the classpath, using 19+ compatibility layer", fullVersion);
131+
if (isGuava_19_0_OrHigher()) {
132+
logger.info("Detected Guava >= 19 in the classpath, using modern compatibility layer");
149133
return new Version19OrHigher();
150-
}
151-
}
152-
153-
private static Manifest loadGuavaManifest() {
154-
InputStream in = null;
155-
try {
156-
Enumeration<URL> resources = Preconditions.class.getClassLoader()
157-
.getResources("META-INF/MANIFEST.MF");
158-
while (resources.hasMoreElements()) {
159-
in = resources.nextElement().openStream();
160-
Manifest manifest = new Manifest(in);
161-
Attributes mainAttributes = manifest.getMainAttributes();
162-
String symbolicName = mainAttributes.getValue("Bundle-SymbolicName");
163-
if ("com.google.guava".equals(symbolicName)) {
164-
return manifest;
165-
}
166-
}
167-
throw new DriverInternalError("Error while looking up Guava manifest: " +
168-
"no manifest with symbolic name 'com.google.guava' found in classpath.");
169-
} catch (Exception e) {
170-
throw new DriverInternalError("Error while looking up Guava manifest", e);
171-
} finally {
172-
if (in != null) {
173-
try {
174-
in.close();
175-
} catch (IOException e) {
176-
// ignore
177-
}
178-
}
179-
}
180-
}
181-
182-
private static String getBundleVersion(Manifest manifest) {
183-
return manifest.getMainAttributes().getValue("Bundle-Version");
184-
}
185-
186-
private static String stripQualifiers(String fullVersion) {
187-
Matcher matcher = GA_VERSION_EXTRACTOR.matcher(fullVersion);
188-
if (matcher.matches()) {
189-
return matcher.group(1);
134+
} else if (isGuava_16_0_1_OrHigher()) {
135+
logger.info("Detected Guava < 19 in the classpath, using legacy compatibility layer");
136+
return new Version18OrLower();
190137
} else {
191-
throw new DriverInternalError(String.format("Could not strip qualifiers from full Guava version %s", fullVersion));
138+
throw new DriverInternalError("Detected incompatible version of Guava in the classpath. " +
139+
"You need 16.0.1 or higher.");
192140
}
193141
}
194142

@@ -272,4 +220,42 @@ public Executor sameThreadExecutor() {
272220
return MoreExecutors.directExecutor();
273221
}
274222
}
223+
224+
private static boolean isGuava_19_0_OrHigher() {
225+
return methodExists(Futures.class, "transformAsync", ListenableFuture.class, AsyncFunction.class);
226+
}
227+
228+
private static boolean isGuava_16_0_1_OrHigher() {
229+
// Cheap check for < 16.0
230+
if (!methodExists(Maps.class, "asConverter", BiMap.class)) {
231+
return false;
232+
}
233+
// More elaborate check to filter out 16.0, which has a bug in TypeToken. We need 16.0.1.
234+
boolean resolved = false;
235+
TypeToken<Map<String, String>> mapOfString = TypeTokens.mapOf(String.class, String.class);
236+
Type type = mapOfString.getType();
237+
if (type instanceof ParameterizedType) {
238+
ParameterizedType pType = (ParameterizedType) type;
239+
Type[] types = pType.getActualTypeArguments();
240+
if (types.length == 2) {
241+
TypeToken valueType = TypeToken.of(types[1]);
242+
resolved = valueType.getRawType().equals(String.class);
243+
}
244+
}
245+
if (!resolved) {
246+
logger.debug("Detected Guava issue #1635 which indicates that version 16.0 is in the classpath");
247+
}
248+
return resolved;
249+
}
250+
251+
private static boolean methodExists(Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
252+
try {
253+
declaringClass.getMethod(methodName, parameterTypes);
254+
return true;
255+
} catch (Exception e) {
256+
logger.debug("Error while checking existence of method " +
257+
declaringClass.getSimpleName() + "." + methodName, e);
258+
return false;
259+
}
260+
}
275261
}

0 commit comments

Comments
 (0)
0