diff --git a/build.gradle b/build.gradle index e2c395b3..6cc6966b 100644 --- a/build.gradle +++ b/build.gradle @@ -28,9 +28,12 @@ subprojects { ext.scalaVersion = projectScalaVersion ? projectScalaVersion : properties.defaultScalaVersion ext.scalaSuffix = getScalaSuffix(ext.scalaVersion) + // Building with non-default protobuf version requires that proto files are re-compiled! + ext.projectProtobufVersion = properties.targetProtobufVersion ? properties.targetProtobufVersion : properties.defaultProtobufVersion + ext.externalDependency = [ 'zookeeper':'org.apache.zookeeper:zookeeper:3.3.4', - 'protobuf':'com.google.protobuf:protobuf-java:2.4.0a', + 'protobuf':"com.google.protobuf:protobuf-java:${ext.projectProtobufVersion}", 'log4j':'log4j:log4j:1.2.17', 'netty':'io.netty:netty:3.7.0.Final', 'slf4jApi':'org.slf4j:slf4j-api:1.7.5', diff --git a/gradle.properties b/gradle.properties index 0cfc53e2..7dfc75ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,3 +2,5 @@ version=0.6.91 defaultScalaVersion=2.10.3 targetScalaVersions=2.10.3 crossBuild=false +defaultProtobufVersion=2.4.0a +targetProtobufVersion=2.4.0a diff --git a/network/src/main/scala/com/linkedin/norbert/network/util/ProtoUtils.scala b/network/src/main/scala/com/linkedin/norbert/network/util/ProtoUtils.scala index 3148af81..5b326f51 100644 --- a/network/src/main/scala/com/linkedin/norbert/network/util/ProtoUtils.scala +++ b/network/src/main/scala/com/linkedin/norbert/network/util/ProtoUtils.scala @@ -12,8 +12,30 @@ import java.lang.reflect.{Field, Constructor} * to bypass those. */ object ProtoUtils extends Logging { + private val ByteStringClass = classOf[ByteString] + // Protobuf 2.6 has protected class LiteralByteString + private val LiteralByteStringClass: Class[ByteString] = try { + val c = classOf[ByteString].getClassLoader().loadClass("com.google.protobuf.LiteralByteString") + c match { + case c2: Class[ByteString] => c2 + case _ => throw new ClassCastException + } + } catch { + case ex: ClassNotFoundException => null + case ex: Exception => + log.warn(ex, "Cannot eliminate a copy when converting a byte[] to a ByteString, failed to find LiteralByteString") + null + } + private val byteStringConstructor: Constructor[ByteString] = try { - val c = classOf[ByteString].getDeclaredConstructor(classOf[Array[Byte]]) + var c:Constructor[ByteString] = null + if(LiteralByteStringClass != null) { + // Protobuf 2.6 + c = LiteralByteStringClass.getDeclaredConstructor(classOf[Array[Byte]]) + }else{ + // Protobuf 2.4 + c = classOf[ByteString].getDeclaredConstructor(classOf[Array[Byte]]) + } c.setAccessible(true) c } catch { @@ -23,7 +45,14 @@ object ProtoUtils extends Logging { } private val byteStringField: Field = try { - val f = classOf[ByteString].getDeclaredField("bytes") + var f:Field = null + if(LiteralByteStringClass != null) { + // Protobuf 2.6 + f = LiteralByteStringClass.getDeclaredField("bytes") + }else{ + // Protobuf 2.4 + f = classOf[ByteString].getDeclaredField("bytes") + } f.setAccessible(true) f } catch { @@ -66,7 +95,15 @@ object ProtoUtils extends Logging { private final def fastByteStringToByteArray(byteString: ByteString): Array[Byte] = { if(byteStringField != null) try { - byteStringField.get(byteString).asInstanceOf[Array[Byte]] + byteString.getClass() match { + case ByteStringClass | LiteralByteStringClass => + // Protobuf 2.4 uses plain ByteString. 2.6 uses LiterealByteString (LiteralByteStringClass is null if 2.4 is used) + byteStringField.get(byteString).asInstanceOf[Array[Byte]] + case _ => + // 2.6 may use "RopeByteString" too.. Not sure if we would actually see that anytime, but warn and use regular method if so. + log.warn("Encountered ByteString of class " + byteString.getClass().getName()+", falling back to safe method") + slowByteStringToByteArray(byteString) + } } catch { case ex: Exception => log.warn(ex, "Encountered exception accessing the private ByteString bytes field, falling back to safe method") @@ -79,4 +116,4 @@ object ProtoUtils extends Logging { private final def slowByteStringToByteArray(byteString: ByteString): Array[Byte] = { byteString.toByteArray } -} \ No newline at end of file +}