|
16 | 16 | package com.datastax.driver.core;
|
17 | 17 |
|
18 | 18 | 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; |
20 | 21 | import com.google.common.reflect.TypeToken;
|
21 | 22 | import com.google.common.util.concurrent.*;
|
22 | 23 | import org.slf4j.Logger;
|
23 | 24 | import org.slf4j.LoggerFactory;
|
24 | 25 |
|
25 |
| -import java.io.IOException; |
26 |
| -import java.io.InputStream; |
| 26 | +import java.lang.reflect.ParameterizedType; |
27 | 27 | import java.lang.reflect.Type;
|
28 |
| -import java.net.URL; |
29 |
| -import java.util.Enumeration; |
| 28 | +import java.util.Map; |
30 | 29 | 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; |
35 | 30 |
|
36 | 31 | /**
|
37 | 32 | * A compatibility layer to support a wide range of Guava versions.
|
|
50 | 45 | public abstract class GuavaCompatibility {
|
51 | 46 |
|
52 | 47 | private static final Logger logger = LoggerFactory.getLogger(GuavaCompatibility.class);
|
53 |
| - private static final Pattern GA_VERSION_EXTRACTOR = Pattern.compile("(\\d+\\.\\d+\\.\\d+).*"); |
54 | 48 |
|
55 | 49 | /**
|
56 | 50 | * 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
|
134 | 128 | public abstract Executor sameThreadExecutor();
|
135 | 129 |
|
136 | 130 | 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"); |
149 | 133 | 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(); |
190 | 137 | } 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."); |
192 | 140 | }
|
193 | 141 | }
|
194 | 142 |
|
@@ -272,4 +220,42 @@ public Executor sameThreadExecutor() {
|
272 | 220 | return MoreExecutors.directExecutor();
|
273 | 221 | }
|
274 | 222 | }
|
| 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 | + } |
275 | 261 | }
|
0 commit comments