diff --git a/changelog/README.md b/changelog/README.md
index b399499f07a..baf1eb4efa8 100644
--- a/changelog/README.md
+++ b/changelog/README.md
@@ -11,6 +11,7 @@
- [improvement] JAVA-1241: Upgrade Netty to 4.1.x.
- [improvement] JAVA-1287: Add CDC to TableOptionsMetadata and Schema Builder.
- [improvement] JAVA-1392: Reduce lock contention in RPTokenFactory.
+- [improvement] JAVA-1328: Provide compatibility with Guava 20.
Merged from 3.1.x branch:
diff --git a/driver-core/pom.xml b/driver-core/pom.xml
index 16af50748f3..4725272b6ef 100644
--- a/driver-core/pom.xml
+++ b/driver-core/pom.xml
@@ -200,7 +200,7 @@
<_include>-osgi.bnd
-
+
jar
@@ -224,7 +224,7 @@
JNR does not provide OSGi bundles, so exclude it; the driver can live without it
Explicitly import javax.security.cert because it's required by Netty, but Netty has been explicitly excluded
-->
-
+
com.datastax.shaded.*
diff --git a/driver-core/src/main/java/com/datastax/driver/core/AbstractAddressableByIndexData.java b/driver-core/src/main/java/com/datastax/driver/core/AbstractAddressableByIndexData.java
index c1b21810e5f..fc795b29578 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/AbstractAddressableByIndexData.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/AbstractAddressableByIndexData.java
@@ -15,7 +15,7 @@
*/
package com.datastax.driver.core;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.reflect.TypeToken;
import java.math.BigDecimal;
@@ -280,7 +280,7 @@ public boolean equals(Object o) {
Object thisValue = this.codecFor(i).deserialize(this.values[i], this.protocolVersion);
Object thatValue = that.codecFor(i).deserialize(that.values[i], that.protocolVersion);
- if (!Objects.equal(thisValue, thatValue))
+ if (!MoreObjects.equal(thisValue, thatValue))
return false;
}
return true;
diff --git a/driver-core/src/main/java/com/datastax/driver/core/AbstractData.java b/driver-core/src/main/java/com/datastax/driver/core/AbstractData.java
index d4144162c26..80eaed6a2c4 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/AbstractData.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/AbstractData.java
@@ -15,7 +15,7 @@
*/
package com.datastax.driver.core;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.reflect.TypeToken;
import java.math.BigDecimal;
@@ -575,7 +575,7 @@ public boolean equals(Object o) {
Object thisValue = this.codecFor(i).deserialize(this.values[i], this.protocolVersion);
Object thatValue = that.codecFor(i).deserialize(that.values[i], that.protocolVersion);
- if (!Objects.equal(thisValue, thatValue))
+ if (!MoreObjects.equal(thisValue, thatValue))
return false;
}
return true;
diff --git a/driver-core/src/main/java/com/datastax/driver/core/AggregateMetadata.java b/driver-core/src/main/java/com/datastax/driver/core/AggregateMetadata.java
index e0717c7f133..635d2538bc0 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/AggregateMetadata.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/AggregateMetadata.java
@@ -16,7 +16,7 @@
package com.datastax.driver.core;
import com.datastax.driver.core.utils.Bytes;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
@@ -376,11 +376,11 @@ public boolean equals(Object other) {
AggregateMetadata that = (AggregateMetadata) other;
return this.keyspace.getName().equals(that.keyspace.getName()) &&
this.argumentTypes.equals(that.argumentTypes) &&
- Objects.equal(this.finalFuncFullName, that.finalFuncFullName) &&
+ MoreObjects.equal(this.finalFuncFullName, that.finalFuncFullName) &&
// Note: this might be a problem if a custom codec has been registered for the initCond's type, with a target Java type that
// does not properly implement equals. We don't have any control over this, at worst this would lead to spurious change
// notifications.
- Objects.equal(this.initCond, that.initCond) &&
+ MoreObjects.equal(this.initCond, that.initCond) &&
this.returnType.equals(that.returnType) &&
this.stateFuncFullName.equals(that.stateFuncFullName) &&
this.stateType.equals(that.stateType);
@@ -390,7 +390,6 @@ public boolean equals(Object other) {
@Override
public int hashCode() {
- return Objects.hashCode(this.keyspace.getName(), this.argumentTypes,
- this.finalFuncFullName, this.initCond, this.returnType, this.stateFuncFullName, this.stateType);
+ return MoreObjects.hashCode(this.keyspace.getName(), this.argumentTypes, this.finalFuncFullName, this.initCond, this.returnType, this.stateFuncFullName, this.stateType);
}
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/CloseFuture.java b/driver-core/src/main/java/com/datastax/driver/core/CloseFuture.java
index c72ae8fc28b..51effb25775 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/CloseFuture.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/CloseFuture.java
@@ -28,7 +28,7 @@
* additional {@link #force} method that can be used to expedite the shutdown
* process (see below).
*
- * Note that this class implements Guava's {@code
+ * Note that this class implements Guava's {@code
* ListenableFuture} and can so be used with Guava's future utilities.
*/
public abstract class CloseFuture extends AbstractFuture {
diff --git a/driver-core/src/main/java/com/datastax/driver/core/Cluster.java b/driver-core/src/main/java/com/datastax/driver/core/Cluster.java
index d4e6ea05bed..54cf8667179 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/Cluster.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/Cluster.java
@@ -64,8 +64,8 @@ public class Cluster implements Closeable {
private static final Logger logger = LoggerFactory.getLogger(Cluster.class);
static {
- // Perform sanity checks to inform user of possible environment misconfiguration.
- SanityChecks.check();
+ // Force initialization to fail fast if there is an issue detecting the version
+ GuavaCompatibility.init();
}
@VisibleForTesting
@@ -334,15 +334,15 @@ public ListenableFuture connectAsync(final String keyspace) {
return sessionInitialized;
} else {
final String useQuery = "USE " + keyspace;
- ListenableFuture keyspaceSet = Futures.transform(sessionInitialized, new AsyncFunction() {
+ ListenableFuture keyspaceSet = GuavaCompatibility.INSTANCE.transformAsync(sessionInitialized, new AsyncFunction() {
@Override
public ListenableFuture apply(Session session) throws Exception {
return session.executeAsync(useQuery);
}
});
- ListenableFuture withErrorHandling = Futures.withFallback(keyspaceSet, new FutureFallback() {
+ ListenableFuture withErrorHandling = GuavaCompatibility.INSTANCE.withFallback(keyspaceSet, new AsyncFunction() {
@Override
- public ListenableFuture create(Throwable t) throws Exception {
+ public ListenableFuture apply(Throwable t) throws Exception {
session.closeAsync();
if (t instanceof SyntaxError) {
// Give a more explicit message, because it's probably caused by a bad keyspace name
@@ -2373,7 +2373,7 @@ public void run() {
rs.getExecutionInfo().setSchemaInAgreement(finalSchemaInAgreement);
future.setResult(rs);
}
- }, MoreExecutors.sameThreadExecutor());
+ }, GuavaCompatibility.INSTANCE.sameThreadExecutor());
} catch (Exception e) {
logger.warn("Error while waiting for schema agreement", e);
diff --git a/driver-core/src/main/java/com/datastax/driver/core/CodecRegistry.java b/driver-core/src/main/java/com/datastax/driver/core/CodecRegistry.java
index e62957f8f71..2e8ef03dfa7 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/CodecRegistry.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/CodecRegistry.java
@@ -16,7 +16,7 @@
package com.datastax.driver.core;
import com.datastax.driver.core.exceptions.CodecNotFoundException;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.cache.*;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.UncheckedExecutionException;
@@ -216,12 +216,12 @@ public boolean equals(Object o) {
if (o == null || getClass() != o.getClass())
return false;
CacheKey cacheKey = (CacheKey) o;
- return Objects.equal(cqlType, cacheKey.cqlType) && Objects.equal(javaType, cacheKey.javaType);
+ return MoreObjects.equal(cqlType, cacheKey.cqlType) && MoreObjects.equal(javaType, cacheKey.javaType);
}
@Override
public int hashCode() {
- return Objects.hashCode(cqlType, javaType);
+ return MoreObjects.hashCode(cqlType, javaType);
}
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/ColumnMetadata.java b/driver-core/src/main/java/com/datastax/driver/core/ColumnMetadata.java
index 0e754721c1d..c4eb68e1b0d 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/ColumnMetadata.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/ColumnMetadata.java
@@ -15,7 +15,7 @@
*/
package com.datastax.driver.core;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import java.util.Arrays;
import java.util.HashMap;
@@ -116,7 +116,7 @@ public boolean equals(Object other) {
@Override
public int hashCode() {
- return Objects.hashCode(name, isStatic, type);
+ return MoreObjects.hashCode(name, isStatic, type);
}
@Override
diff --git a/driver-core/src/main/java/com/datastax/driver/core/Connection.java b/driver-core/src/main/java/com/datastax/driver/core/Connection.java
index fc27f1262da..04727f8d388 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/Connection.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/Connection.java
@@ -18,8 +18,8 @@
import com.datastax.driver.core.Responses.Result.SetKeyspace;
import com.datastax.driver.core.exceptions.*;
import com.datastax.driver.core.utils.MoreFutures;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
import com.google.common.util.concurrent.*;
@@ -179,13 +179,13 @@ public void operationComplete(ChannelFuture future) throws Exception {
Executor initExecutor = factory.manager.configuration.getPoolingOptions().getInitializationExecutor();
- ListenableFuture initializeTransportFuture = Futures.transform(channelReadyFuture,
+ ListenableFuture initializeTransportFuture = GuavaCompatibility.INSTANCE.transformAsync(channelReadyFuture,
onChannelReady(protocolVersion, initExecutor), initExecutor);
// Fallback on initializeTransportFuture so we can properly propagate specific exceptions.
- ListenableFuture initFuture = Futures.withFallback(initializeTransportFuture, new FutureFallback() {
+ ListenableFuture initFuture = GuavaCompatibility.INSTANCE.withFallback(initializeTransportFuture, new AsyncFunction() {
@Override
- public ListenableFuture create(Throwable t) throws Exception {
+ public ListenableFuture apply(Throwable t) throws Exception {
SettableFuture future = SettableFuture.create();
// Make sure the connection gets properly closed.
if (t instanceof ClusterNameMismatchException || t instanceof UnsupportedProtocolVersionException) {
@@ -233,7 +233,7 @@ private AsyncFunction onChannelReady(final ProtocolVersion protocolV
public ListenableFuture apply(Void input) throws Exception {
ProtocolOptions.Compression compression = factory.configuration.getProtocolOptions().getCompression();
Future startupResponseFuture = write(new Requests.Startup(compression));
- return Futures.transform(startupResponseFuture,
+ return GuavaCompatibility.INSTANCE.transformAsync(startupResponseFuture,
onStartupResponse(protocolVersion, initExecutor), initExecutor);
}
};
@@ -297,7 +297,7 @@ private ListenableFuture checkClusterName(ProtocolVersion protocolVersion,
DefaultResultSetFuture clusterNameFuture = new DefaultResultSetFuture(null, protocolVersion, new Requests.Query("select cluster_name from system.local"));
try {
write(clusterNameFuture);
- return Futures.transform(clusterNameFuture,
+ return GuavaCompatibility.INSTANCE.transformAsync(clusterNameFuture,
new AsyncFunction() {
@Override
public ListenableFuture apply(ResultSet rs) throws Exception {
@@ -323,7 +323,7 @@ private ListenableFuture authenticateV1(Authenticator authenticator, final
Requests.Credentials creds = new Requests.Credentials(((ProtocolV1Authenticator) authenticator).getCredentials());
try {
Future authResponseFuture = write(creds);
- return Futures.transform(authResponseFuture,
+ return GuavaCompatibility.INSTANCE.transformAsync(authResponseFuture,
new AsyncFunction() {
@Override
public ListenableFuture apply(Message.Response authResponse) throws Exception {
@@ -350,7 +350,7 @@ private ListenableFuture authenticateV2(final Authenticator authenticator,
try {
Future authResponseFuture = write(new Requests.AuthResponse(initialResponse));
- return Futures.transform(authResponseFuture, onV2AuthResponse(authenticator, protocolVersion, executor), executor);
+ return GuavaCompatibility.INSTANCE.transformAsync(authResponseFuture, onV2AuthResponse(authenticator, protocolVersion, executor), executor);
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
@@ -376,7 +376,7 @@ public ListenableFuture apply(Message.Response authResponse) throws Except
// Otherwise, send the challenge response back to the server
logger.trace("{} Sending Auth response to challenge", this);
Future nextResponseFuture = write(new Requests.AuthResponse(responseToServer));
- return Futures.transform(nextResponseFuture, onV2AuthResponse(authenticator, protocolVersion, executor), executor);
+ return GuavaCompatibility.INSTANCE.transformAsync(nextResponseFuture, onV2AuthResponse(authenticator, protocolVersion, executor), executor);
}
case ERROR:
// This is not very nice, but we're trying to identify if we
@@ -471,7 +471,7 @@ void setKeyspace(String keyspace) throws ConnectionException {
if (keyspace == null)
return;
- if (Objects.equal(keyspace(), keyspace))
+ if (MoreObjects.equal(keyspace(), keyspace))
return;
try {
@@ -497,7 +497,7 @@ void setKeyspace(String keyspace) throws ConnectionException {
ListenableFuture setKeyspaceAsync(final String keyspace) throws ConnectionException, BusyConnectionException {
SetKeyspaceAttempt existingAttempt = targetKeyspace.get();
- if (Objects.equal(existingAttempt.keyspace, keyspace))
+ if (MoreObjects.equal(existingAttempt.keyspace, keyspace))
return existingAttempt.future;
final SettableFuture ksFuture = SettableFuture.create();
diff --git a/driver-core/src/main/java/com/datastax/driver/core/ControlConnection.java b/driver-core/src/main/java/com/datastax/driver/core/ControlConnection.java
index d785b8e057a..1483190abb2 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/ControlConnection.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/ControlConnection.java
@@ -16,8 +16,8 @@
package com.datastax.driver.core;
import com.datastax.driver.core.exceptions.*;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
import com.google.common.util.concurrent.ListenableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -519,7 +519,7 @@ private static void updateInfo(Host host, Row row, Cluster.Manager cluster, bool
}
private static void updateLocationInfo(Host host, String datacenter, String rack, boolean isInitialConnection, Cluster.Manager cluster) {
- if (Objects.equal(host.getDatacenter(), datacenter) && Objects.equal(host.getRack(), rack))
+ if (MoreObjects.equal(host.getDatacenter(), datacenter) && MoreObjects.equal(host.getRack(), rack))
return;
// If the dc/rack information changes for an existing node, we need to update the load balancing policy.
diff --git a/driver-core/src/main/java/com/datastax/driver/core/DataType.java b/driver-core/src/main/java/com/datastax/driver/core/DataType.java
index 4849f6367c0..f92f4255371 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/DataType.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/DataType.java
@@ -16,7 +16,7 @@
package com.datastax.driver.core;
import com.datastax.driver.core.exceptions.DriverInternalError;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.netty.buffer.ByteBuf;
@@ -662,7 +662,7 @@ public List getTypeArguments() {
@Override
public final int hashCode() {
- return Objects.hashCode(name, typeArguments);
+ return MoreObjects.hashCode(name, typeArguments);
}
@Override
@@ -740,7 +740,7 @@ public String getCustomTypeClassName() {
@Override
public final int hashCode() {
- return Objects.hashCode(name, customClassName);
+ return MoreObjects.hashCode(name, customClassName);
}
@Override
@@ -749,7 +749,7 @@ public final boolean equals(Object o) {
return false;
DataType.CustomType d = (DataType.CustomType) o;
- return name == d.name && Objects.equal(customClassName, d.customClassName);
+ return name == d.name && MoreObjects.equal(customClassName, d.customClassName);
}
@Override
diff --git a/driver-core/src/main/java/com/datastax/driver/core/FunctionMetadata.java b/driver-core/src/main/java/com/datastax/driver/core/FunctionMetadata.java
index fdbfdaab522..523dfeb9718 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/FunctionMetadata.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/FunctionMetadata.java
@@ -15,7 +15,7 @@
*/
package com.datastax.driver.core;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -321,6 +321,6 @@ public boolean equals(Object other) {
@Override
public int hashCode() {
- return Objects.hashCode(keyspace.getName(), arguments, body, calledOnNullInput, language, returnType);
+ return MoreObjects.hashCode(keyspace.getName(), arguments, body, calledOnNullInput, language, returnType);
}
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/GuavaCompatibility.java b/driver-core/src/main/java/com/datastax/driver/core/GuavaCompatibility.java
new file mode 100644
index 00000000000..9ff0de2ff69
--- /dev/null
+++ b/driver-core/src/main/java/com/datastax/driver/core/GuavaCompatibility.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2012-2015 DataStax Inc.
+ *
+ * Licensed 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.driver.core;
+
+import com.datastax.driver.core.exceptions.DriverInternalError;
+import com.google.common.base.Preconditions;
+import com.google.common.reflect.TypeToken;
+import com.google.common.util.concurrent.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A compatibility layer to support a wide range of Guava versions.
+ *
+ * The driver is compatible with Guava 16.0.1 or higher, but Guava 20 introduced incompatible breaking changes in its
+ * API, that could in turn be breaking for legacy driver clients if we simply upgraded our dependency. We don't want to
+ * increment our major version "just" for Guava (we have other changes planned).
+ *
+ * Therefore we depend on Guava 19, which has both the deprecated and the new APIs, and detect the actual version at
+ * runtime in order to call the relevant methods.
+ *
+ * This is a hack, and might not work with subsequent Guava releases; the real fix is to stop exposing Guava in our
+ * public API. We'll address that in version 4 of the driver.
+ */
+@SuppressWarnings("deprecation")
+public abstract class GuavaCompatibility {
+
+ private static final Logger logger = LoggerFactory.getLogger(GuavaCompatibility.class);
+ private static final Pattern GA_VERSION_EXTRACTOR = Pattern.compile("(\\d+\\.\\d+\\.\\d+).*");
+
+ /**
+ * The unique instance of this class, that is compatible with the Guava version found in the classpath.
+ */
+ public static final GuavaCompatibility INSTANCE = selectImplementation();
+
+ /**
+ * Force the initialization of the class. This should be called early to ensure a fast failure if an incompatible
+ * version of Guava is in the classpath (the driver code calls it when loading the {@link Cluster} class).
+ */
+ public static void init() {
+ // nothing to do, we just want the static initializers to run
+ }
+
+ /**
+ * Returns a {@code Future} whose result is taken from the given primary
+ * {@code input} or, if the primary input fails, from the {@code Future}
+ * provided by the {@code fallback}.
+ *
+ * @see Futures#withFallback(ListenableFuture, FutureFallback)
+ * @see Futures#catchingAsync(ListenableFuture, Class, AsyncFunction)
+ */
+ public abstract ListenableFuture withFallback(ListenableFuture extends V> input,
+ AsyncFunction fallback);
+
+ /**
+ * Returns a {@code Future} whose result is taken from the given primary
+ * {@code input} or, if the primary input fails, from the {@code Future}
+ * provided by the {@code fallback}.
+ *
+ * @see Futures#withFallback(ListenableFuture, FutureFallback, Executor)
+ * @see Futures#catchingAsync(ListenableFuture, Class, AsyncFunction, Executor)
+ */
+ public abstract ListenableFuture withFallback(ListenableFuture extends V> input,
+ AsyncFunction fallback, Executor executor);
+
+ /**
+ * Returns a new {@code ListenableFuture} whose result is asynchronously
+ * derived from the result of the given {@code Future}. More precisely, the
+ * returned {@code Future} takes its result from a {@code Future} produced by
+ * applying the given {@code AsyncFunction} to the result of the original
+ * {@code Future}.
+ *
+ * @see Futures#transform(ListenableFuture, AsyncFunction)
+ * @see Futures#transformAsync(ListenableFuture, AsyncFunction)
+ */
+ public abstract ListenableFuture transformAsync(ListenableFuture input,
+ AsyncFunction super I, ? extends O> function);
+
+ /**
+ * Returns a new {@code ListenableFuture} whose result is asynchronously
+ * derived from the result of the given {@code Future}. More precisely, the
+ * returned {@code Future} takes its result from a {@code Future} produced by
+ * applying the given {@code AsyncFunction} to the result of the original
+ * {@code Future}.
+ *
+ * @see Futures#transform(ListenableFuture, AsyncFunction, Executor)
+ * @see Futures#transformAsync(ListenableFuture, AsyncFunction, Executor)
+ */
+ public abstract ListenableFuture transformAsync(ListenableFuture input,
+ AsyncFunction super I, ? extends O> function,
+ Executor executor);
+
+ /**
+ * Returns true if {@code target} is a supertype of {@code argument}. "Supertype" is defined
+ * according to the rules for type arguments introduced with Java generics.
+ *
+ * @see TypeToken#isAssignableFrom(Type)
+ * @see TypeToken#isSupertypeOf(Type)
+ */
+ public abstract boolean isSupertypeOf(TypeToken> target, TypeToken> argument);
+
+ /**
+ * Returns an {@link Executor} that runs each task in the thread that invokes
+ * {@link Executor#execute execute}, as in {@link ThreadPoolExecutor.CallerRunsPolicy}.
+ *
+ * @see MoreExecutors#sameThreadExecutor()
+ * @see MoreExecutors#directExecutor()
+ */
+ public abstract Executor sameThreadExecutor();
+
+ private static GuavaCompatibility selectImplementation() {
+ String fullVersion = getBundleVersion(loadGuavaManifest());
+ // Get rid of potential rc qualifier, as it could throw off the lexical comparisons
+ String version = stripQualifiers(fullVersion);
+ if (version.compareTo("16.0.1") < 0) {
+ throw new DriverInternalError(String.format(
+ "Detected incompatible version of Guava in the classpath (%s). " +
+ "You need 16.0.1 or higher.", fullVersion));
+ } else if (version.compareTo("19.0") < 0) {
+ logger.info("Detected Guava {} in the classpath, using pre-19 compatibility layer", fullVersion);
+ return new Version18OrLower();
+ } else {
+ logger.info("Detected Guava {} in the classpath, using 19+ compatibility layer", fullVersion);
+ return new Version19OrHigher();
+ }
+ }
+
+ private static Manifest loadGuavaManifest() {
+ InputStream in = null;
+ try {
+ Enumeration resources = Preconditions.class.getClassLoader()
+ .getResources("META-INF/MANIFEST.MF");
+ while (resources.hasMoreElements()) {
+ in = resources.nextElement().openStream();
+ Manifest manifest = new Manifest(in);
+ Attributes mainAttributes = manifest.getMainAttributes();
+ String symbolicName = mainAttributes.getValue("Bundle-SymbolicName");
+ if ("com.google.guava".equals(symbolicName)) {
+ return manifest;
+ }
+ }
+ throw new DriverInternalError("Error while looking up Guava manifest: " +
+ "no manifest with symbolic name 'com.google.guava' found in classpath.");
+ } catch (Exception e) {
+ throw new DriverInternalError("Error while looking up Guava manifest", e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ private static String getBundleVersion(Manifest manifest) {
+ return manifest.getMainAttributes().getValue("Bundle-Version");
+ }
+
+ private static String stripQualifiers(String fullVersion) {
+ Matcher matcher = GA_VERSION_EXTRACTOR.matcher(fullVersion);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ } else {
+ throw new DriverInternalError(String.format("Could not strip qualifiers from full Guava version %s", fullVersion));
+ }
+ }
+
+ private static class Version18OrLower extends GuavaCompatibility {
+
+ @Override
+ public ListenableFuture withFallback(ListenableFuture extends V> input,
+ final AsyncFunction fallback) {
+ return Futures.withFallback(input, new FutureFallback() {
+ @Override
+ public ListenableFuture create(Throwable t) throws Exception {
+ return fallback.apply(t);
+ }
+ });
+ }
+
+ @Override
+ public ListenableFuture withFallback(ListenableFuture extends V> input,
+ final AsyncFunction fallback,
+ Executor executor) {
+ return Futures.withFallback(input, new FutureFallback() {
+ @Override
+ public ListenableFuture create(Throwable t) throws Exception {
+ return fallback.apply(t);
+ }
+ }, executor);
+ }
+
+ @Override
+ public ListenableFuture transformAsync(ListenableFuture input, AsyncFunction super I, ? extends O> function) {
+ return Futures.transform(input, function);
+ }
+
+ @Override
+ public ListenableFuture transformAsync(ListenableFuture input, AsyncFunction super I, ? extends O> function, Executor executor) {
+ return Futures.transform(input, function, executor);
+ }
+
+ @Override
+ public boolean isSupertypeOf(TypeToken> target, TypeToken> argument) {
+ return target.isAssignableFrom(argument);
+ }
+
+ @Override
+ public Executor sameThreadExecutor() {
+ return MoreExecutors.sameThreadExecutor();
+ }
+ }
+
+ private static class Version19OrHigher extends GuavaCompatibility {
+
+ @Override
+ public ListenableFuture withFallback(ListenableFuture extends V> input,
+ AsyncFunction fallback) {
+ return Futures.catchingAsync(input, Throwable.class, fallback);
+ }
+
+ @Override
+ public ListenableFuture withFallback(ListenableFuture extends V> input,
+ AsyncFunction fallback, Executor executor) {
+ return Futures.catchingAsync(input, Throwable.class, fallback, executor);
+ }
+
+ @Override
+ public ListenableFuture transformAsync(ListenableFuture input, AsyncFunction super I, ? extends O> function) {
+ return Futures.transformAsync(input, function);
+ }
+
+ @Override
+ public ListenableFuture transformAsync(ListenableFuture input, AsyncFunction super I, ? extends O> function, Executor executor) {
+ return Futures.transformAsync(input, function, executor);
+ }
+
+ @Override
+ public boolean isSupertypeOf(TypeToken> target, TypeToken> argument) {
+ return target.isSupertypeOf(argument);
+ }
+
+ @Override
+ public Executor sameThreadExecutor() {
+ return MoreExecutors.directExecutor();
+ }
+ }
+}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/HostConnectionPool.java b/driver-core/src/main/java/com/datastax/driver/core/HostConnectionPool.java
index 4d80ea57f2b..1dcfb336e6e 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/HostConnectionPool.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/HostConnectionPool.java
@@ -167,9 +167,9 @@ public void onFailure(Throwable t) {
}
private ListenableFuture handleErrors(ListenableFuture connectionInitFuture, Executor executor) {
- return Futures.withFallback(connectionInitFuture, new FutureFallback() {
+ return GuavaCompatibility.INSTANCE.withFallback(connectionInitFuture, new AsyncFunction() {
@Override
- public ListenableFuture create(Throwable t) throws Exception {
+ public ListenableFuture apply(Throwable t) throws Exception {
// Propagate these exceptions because they mean no connection will ever succeed. They will be handled
// accordingly in SessionManager#maybeAddPool.
Throwables.propagateIfInstanceOf(t, ClusterNameMismatchException.class);
@@ -639,7 +639,7 @@ public void run() {
if (connection.state.compareAndSet(OPEN, GONE))
open.decrementAndGet();
}
- }, MoreExecutors.sameThreadExecutor());
+ }, GuavaCompatibility.INSTANCE.sameThreadExecutor());
futures.add(future);
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/IndexMetadata.java b/driver-core/src/main/java/com/datastax/driver/core/IndexMetadata.java
index bf393104997..bf346b530b5 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/IndexMetadata.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/IndexMetadata.java
@@ -15,7 +15,7 @@
*/
package com.datastax.driver.core;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
@@ -240,7 +240,7 @@ public boolean apply(Map.Entry input) {
}
public int hashCode() {
- return Objects.hashCode(name, kind, target, options);
+ return MoreObjects.hashCode(name, kind, target, options);
}
public boolean equals(Object obj) {
@@ -252,10 +252,10 @@ public boolean equals(Object obj) {
IndexMetadata other = (IndexMetadata) obj;
- return Objects.equal(name, other.name)
- && Objects.equal(kind, other.kind)
- && Objects.equal(target, other.target)
- && Objects.equal(options, other.options);
+ return MoreObjects.equal(name, other.name)
+ && MoreObjects.equal(kind, other.kind)
+ && MoreObjects.equal(target, other.target)
+ && MoreObjects.equal(options, other.options);
}
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/MaterializedViewMetadata.java b/driver-core/src/main/java/com/datastax/driver/core/MaterializedViewMetadata.java
index 7132328abfd..952541cb15e 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/MaterializedViewMetadata.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/MaterializedViewMetadata.java
@@ -16,7 +16,7 @@
package com.datastax.driver.core;
-import com.google.common.base.Objects;
+import com.datastax.driver.core.utils.MoreObjects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -221,20 +221,19 @@ public boolean equals(Object other) {
return false;
MaterializedViewMetadata that = (MaterializedViewMetadata) other;
- return Objects.equal(this.name, that.name) &&
- Objects.equal(this.id, that.id) &&
- Objects.equal(this.partitionKey, that.partitionKey) &&
- Objects.equal(this.clusteringColumns, that.clusteringColumns) &&
- Objects.equal(this.columns, that.columns) &&
- Objects.equal(this.options, that.options) &&
- Objects.equal(this.clusteringOrder, that.clusteringOrder) &&
- Objects.equal(this.baseTable.getName(), that.baseTable.getName()) &&
+ return MoreObjects.equal(this.name, that.name) &&
+ MoreObjects.equal(this.id, that.id) &&
+ MoreObjects.equal(this.partitionKey, that.partitionKey) &&
+ MoreObjects.equal(this.clusteringColumns, that.clusteringColumns) &&
+ MoreObjects.equal(this.columns, that.columns) &&
+ MoreObjects.equal(this.options, that.options) &&
+ MoreObjects.equal(this.clusteringOrder, that.clusteringOrder) &&
+ MoreObjects.equal(this.baseTable.getName(), that.baseTable.getName()) &&
this.includeAllColumns == that.includeAllColumns;
}
@Override
public int hashCode() {
- return Objects.hashCode(name, id, partitionKey, clusteringColumns, columns, options, clusteringOrder,
- baseTable.getName(), includeAllColumns);
+ return MoreObjects.hashCode(name, id, partitionKey, clusteringColumns, columns, options, clusteringOrder, baseTable.getName(), includeAllColumns);
}
}
diff --git a/driver-core/src/main/java/com/datastax/driver/core/PoolingOptions.java b/driver-core/src/main/java/com/datastax/driver/core/PoolingOptions.java
index 38a080cbe37..ec6544a3d20 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/PoolingOptions.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/PoolingOptions.java
@@ -17,7 +17,6 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.MoreExecutors;
import java.util.Map;
import java.util.concurrent.Executor;
@@ -142,7 +141,7 @@ public class PoolingOptions {
*/
public static final int DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 30;
- private static final Executor DEFAULT_INITIALIZATION_EXECUTOR = MoreExecutors.sameThreadExecutor();
+ private static final Executor DEFAULT_INITIALIZATION_EXECUTOR = GuavaCompatibility.INSTANCE.sameThreadExecutor();
private volatile Cluster.Manager manager;
private volatile ProtocolVersion protocolVersion;
diff --git a/driver-core/src/main/java/com/datastax/driver/core/ResultSetFuture.java b/driver-core/src/main/java/com/datastax/driver/core/ResultSetFuture.java
index 52f9adeea1f..f91f3a10f81 100644
--- a/driver-core/src/main/java/com/datastax/driver/core/ResultSetFuture.java
+++ b/driver-core/src/main/java/com/datastax/driver/core/ResultSetFuture.java
@@ -26,7 +26,7 @@
/**
* A future on a {@link ResultSet}.
*
- * Note that this class implements Guava's {@code
+ * Note that this class implements Guava's {@code
* ListenableFuture} and can so be used with Guava's future utilities.
*/
public interface ResultSetFuture extends ListenableFuture {
diff --git a/driver-core/src/main/java/com/datastax/driver/core/SanityChecks.java b/driver-core/src/main/java/com/datastax/driver/core/SanityChecks.java
deleted file mode 100644
index c93020b4692..00000000000
--- a/driver-core/src/main/java/com/datastax/driver/core/SanityChecks.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2012-2015 DataStax Inc.
- *
- * Licensed 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.driver.core;
-
-import com.google.common.reflect.TypeToken;
-
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.Map;
-
-class SanityChecks {
-
- /**
- * Performs a series of runtime checks to ensure the environment does not have any
- * incompatible libraries or configurations. Depending on the severity of an
- * incompatibility an {@link IllegalStateException} may be thrown or an ERROR or
- * WARNING is logged.
- *
- * @throws IllegalStateException If an environment incompatibility is detected.
- * @see #checkGuava
- */
- static void check() {
- checkGuava();
- }
-
- /**
- * Detects if a version of guava older than 16.01 is present by attempting to create
- * a {@link TypeToken} instance for Map<String,String> and ensures that the
- * value type argument is of instance {@link String}. If using an older version of guava
- * this will resolve to {@link Object} instead. In this case an {@link IllegalStateException}
- * is thrown.
- *
- * @throws IllegalStateException if version of guava less than 16.01 is detected.
- */
- static void checkGuava() {
- boolean resolved = false;
- TypeToken