Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#76] Protocol v3 & v4 support, serialization/deserialization rewrite using scodec #171

Merged
merged 58 commits into from
Jan 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
044c6b1
Initial commit of scodec-based protocol parser.
tolbertam Jul 12, 2015
94231e8
Initial commit of scodec changes.
tolbertam Aug 5, 2016
719aed8
Delete some tests (to be replaced).
tolbertam Aug 5, 2016
373acbc
Fixes
tolbertam Aug 5, 2016
e686b60
Remove JDK7 specific annotations
tolbertam Aug 5, 2016
feebe73
Update driver dependencies.
tolbertam Aug 5, 2016
2197d00
Remove scodec-akka dependency since it requires JDK8
tolbertam Aug 5, 2016
b3c5f42
Fix java-it-tests source compatibility
tolbertam Aug 5, 2016
af7edbf
Fallback on toString for Any in String codecs.
tolbertam Aug 5, 2016
7aaf41b
Fix batch id parsing.
tolbertam Aug 5, 2016
70769d7
Replace duplicate batch primes.
tolbertam Aug 5, 2016
77a2e4d
handle null values to json.
tolbertam Aug 5, 2016
4f09b0b
Expect variables even if no prime is matched (enhancement).
tolbertam Aug 5, 2016
0ca3ad6
Grab variable types from preparedMetadata.
tolbertam Aug 8, 2016
62e76b0
Default consistency / column types like before.
tolbertam Aug 8, 2016
a1ec36a
Expect query parmeters as they are now recorded (weren't before).
tolbertam Aug 8, 2016
f078c0c
Default variable types on prime incoming.
tolbertam Aug 8, 2016
49ddae9
Various fixes for protocol version nuances.
tolbertam Aug 8, 2016
e5883f1
Update tests to consider implicit protocol version.
tolbertam Aug 9, 2016
9c64e09
Remove printlns
tolbertam Aug 9, 2016
859f98c
Address some TODOs, remove obsolete ones.
tolbertam Aug 9, 2016
c6418e2
Bump to 1.1.0
tolbertam Aug 9, 2016
4644feb
Revert logger changes.
tolbertam Aug 9, 2016
a472162
TupleCodec prototype.
tolbertam Aug 16, 2016
3019f07
Readd ServerStubRunnerIntegrationTest
tolbertam Aug 17, 2016
f1dc31a
Readd PrimingJsonHelperTest
tolbertam Aug 17, 2016
198e204
Add some DataType tests
tolbertam Aug 29, 2016
5a2687b
Add license headers.
tolbertam Sep 5, 2016
ecba5e3
More DataType tests.
tolbertam Sep 5, 2016
f4c3530
Allow OPTIONS messages to be sent before STARTUP.
tolbertam Oct 4, 2016
01c2a28
Handle Protocol V1 query appropriately
tolbertam Oct 8, 2016
2a71c06
Special case text using Varchar codec.
tolbertam Oct 8, 2016
7849cfb
ProtocolVersion DataType codec support.
tolbertam Oct 8, 2016
16dca4c
Remove QueryFlags in favor of deriving them.
tolbertam Oct 9, 2016
17297d5
Improve tests for QueryParameters, fix bug
tolbertam Oct 9, 2016
785f6fa
Remove scodec-akka dependency (isn't needed)
tolbertam Oct 11, 2016
13a16ba
Update cql-antlr to support v3 and v4 types
tolbertam Oct 12, 2016
f4f5e42
Nested collection support in cql-antlr.
tolbertam Oct 12, 2016
076f074
Update collection priming tests for nested collection validation
tolbertam Oct 12, 2016
e20c53d
Add integration tests for protocol v3 and v4 types
tolbertam Oct 13, 2016
641c746
Consider common-v3 for JDK selection in travis build.
tolbertam Oct 13, 2016
ca3565c
Implementation and Tests for protocol v4 error messages
tolbertam Oct 13, 2016
48e691d
Fix bug where the queryPatternPrimes were not showing up
tolbertam Oct 13, 2016
318ccc2
Add documentation for protocol v3 and v4 changes.
tolbertam Oct 13, 2016
ed2cd8e
Avoid repeated creation of codecs by binding them to ProtocolVersion
tolbertam Oct 13, 2016
dffcfdb
Cache dataType codecs.
tolbertam Oct 14, 2016
69791ce
Fix and simplify Batch codec, add Message specs
tolbertam Oct 14, 2016
79f1b33
Fix PreparedMetadata, tests for Result
tolbertam Oct 14, 2016
4f7534a
Fix compilation issues introduced by previous commit.
tolbertam Oct 14, 2016
0882ad9
Revert logic to treat RowMetadata as an Option.
tolbertam Oct 14, 2016
b919f79
Optimize FrameHeader codec by tying it to ProtocolVersion.
tolbertam Oct 14, 2016
c65a11d
Cache Row codecs and add more documentation.
tolbertam Oct 15, 2016
239005f
More documentation of codec module.
tolbertam Oct 15, 2016
ea0b084
Update readme to include new modules and note protocol version support.
tolbertam Oct 15, 2016
462eff8
Force java 1.6 for codec module. Update some URLs in gradle.
tolbertam Oct 15, 2016
023eaa8
Make more clear that standalone jar is the one that should be used.
tolbertam Oct 16, 2016
0db09d1
Use spray-routing-shapeless23 to prevent conflict with scodec.
tolbertam Nov 19, 2016
83b90f0
Changes based on review feedback.
tolbertam Jan 5, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ sudo: false
install: ./gradlew assemble -x signArchives
script:
- jdk_switcher use openjdk6
- ./gradlew check -x signArchives -x java-it-tests:common:check -x java-it-tests:driver20:check -x java-it-tests:driver21:check -x java-it-tests:driver30:check
- ./gradlew check -x signArchives -x java-it-tests:common:check -x java-it-tests:common-v3:check -x java-it-tests:driver20:check -x java-it-tests:driver21:check -x java-it-tests:driver30:check
- jdk_switcher use oraclejdk8
- ./gradlew java-it-tests:common:check java-it-tests:driver20:check java-it-tests:driver21:check java-it-tests:driver30:check -x signArchives
- ./gradlew java-it-tests:common:check java-it-tests:common-v3:check java-it-tests:driver20:check java-it-tests:driver21:check java-it-tests:driver30:check -x signArchives

cache:
directories:
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Scassandra - Making testing Cassandra easy
# Scassandra - Making testing Apache Cassandra easy


## Migrating to Version 1.*
Expand All @@ -16,7 +16,9 @@ HTTP JSON API:
![TravisCI](https://travis-ci.org/scassandra/scassandra-server.svg?branch=master)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/scassandra/scassandra-server?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

Datastax Java 2.2, 2.1, 2.0 and 1.0 support.
Supports Apache Cassandra Protocol Versions 1 through 4.

Datastax Java 3.x and 2.x support.

Stubbed Cassandra runs as a separate process that your application will believe is a real Cassandra node.
It does this by implementing the server side of the binary protocol.
Expand All @@ -38,6 +40,8 @@ To run all tests:

It is made up of:

* cql-antlr - parses cql types using an [antlr4](http://www.antlr.org/) grammar.
* codec - parses Apache Cassandra's native protocol using [scodec](http://scodec.org/).
* server - accepts requests over the native protocol and priming over JSON/HTTP. Written in Scala
* java-client - wrapper for starting the sever in the same JVM as unit/integration tests and a Java API for the priming endpoinds
* java-it-tests - integration tests against multiple versions of the DataStax Java driver
Expand Down
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ subprojects {
akkaVersion = '2.3.9'
commonsCodecVersion = '1.10'
httpClientVersion = '4.3.3'
javaDriverVersion = '2.0.10'
javaDriverVersion = '3.1.1'
junitVersion = '4.11'
guavaVersion = '17.0'
gsonVersion = '2.5'
logbackVersion = '1.1.1'
mockitoVersion = '1.9.5'
scalaVersion = '2.11.7'
scalaVersion = '2.11.8'
scodecVersion = '1.10.2'
slf4jVersion = '1.7.10'
sprayVersion = '1.3.3'
sprayJsonVersion = '1.3.2'
Expand Down
137 changes: 137 additions & 0 deletions codec/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
buildscript {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
maven { url "https://oss.sonatype.org/content/groups/public" }
}
dependencies {
classpath "com.github.maiflai:gradle-scalatest:0.7"
}
}

apply plugin: "com.github.maiflai.scalatest"
apply plugin: 'scala'
apply plugin: 'signing'
apply plugin: 'maven'

group 'org.scassandra'
jar.baseName = 'scassandra-codec_2.11'

if (!project.hasProperty("ossrhUsername")) {
ext.ossrhUsername = "dummy"
}
if (!project.hasProperty("ossrhPassword")) {
ext.ossrhPassword = "dummy"
}

repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
maven {
url "https://oss.sonatype.org/content/groups/staging"
}
}

compileJava {
sourceCompatibility = "1.6"
targetCompatibility = "1.6"
}


compileScala {
sourceCompatibility = "1.6"
targetCompatibility = "1.6"
}

task sourceJar(type: Jar) {
classifier = 'sources'
baseName = 'scassandra-codec_2.11'
from sourceSets.main.allScala
}

task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
baseName = 'scassandra-codec'
excludes = ['**/**']
from 'build/docs/javadoc'
}

artifacts {
archives jar
archives javadocJar
archives sourceJar
}

signing {
sign configurations.archives
}

uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }

repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}

snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots") {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.artifactId = 'scassandra-codec_2.11'

pom.project {
name 'Scassandra codec'
packaging 'jar'
// optionally artifactId can be defined here
description 'Scassandra codec'
url 'https://github.com/scassandra/'

scm {
connection 'scm:git:[email protected]:scassandra/scassandra-server.git'
developerConnection '[email protected]:scassandra/scassandra-server.git'
url 'https://github.com/scassandra/scassandra-server'
}

licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}

developers {
developer {
id 'chbatey'
name 'Christopher Batey'
email '[email protected]'
}
developer {
id 'tepafoo'
name 'Dogan Narinc'
email '[email protected]'
}
}
}
}
}
}

dependencies {
compile "org.scala-lang:scala-library:$scalaVersion"
compile "org.scodec:scodec-core_2.11:$scodecVersion"
compile "com.google.guava:guava:$guavaVersion"

testCompile 'org.scalacheck:scalacheck_2.11:1.12.4'
testCompile "org.scalatest:scalatest_2.11:2.2.3"

testRuntime 'org.pegdown:pegdown:1.1.0'
}

test {
maxParallelForks = 1
}
44 changes: 44 additions & 0 deletions codec/src/main/scala/org/scassandra/codec/Frame.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2016 Christopher Batey and Dogan Narinc
*
* 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 org.scassandra.codec

import scodec.Codec
import scodec.codecs._

/**
* Defines a Frame, the top level payload in the spec. See section one of the spec.
*
* A Frame is composed of a header which indicates information about the body, and the message body itself.
*
* @param header Header of the message
* @param message The message itself.
*/
case class Frame(
header: FrameHeader,
message: Message
)

// TODO: Support warnings, custom payload, and tracing.

object Frame {
/**
* Codec for [[Frame]], reads header and then uses the [[FrameHeader]] to determine the [[Message]].
*/
implicit val codec: Codec[Frame] = {
("header" | FrameHeader.codec) flatPrepend { header =>
("message" | Message.codec(header.opcode)(header.version.version)).hlist
}}.as[Frame]
}
141 changes: 141 additions & 0 deletions codec/src/main/scala/org/scassandra/codec/FrameHeader.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright (C) 2016 Christopher Batey and Dogan Narinc
*
* 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 org.scassandra.codec

import scodec.Codec
import scodec.codecs._

/**
* Indicates the direction of a frame.
*/
sealed trait MessageDirection

/**
* Represents the 'request' direction (0).
*/
case object Request extends MessageDirection

/**
* Represents the 'response' direction (1).
*/
case object Response extends MessageDirection

object MessageDirection {
/**
* 1 bit representation of the direction of a frame. If 0, it's a request, else response.
*/
implicit val codec: Codec[MessageDirection] = mappedEnum(bool,
Request -> false,
Response -> true
)
}

/**
* Represents the first byte in the frame, which indicates the [[ProtocolVersion]] and [[MessageDirection]]
* @param direction direction of the frame
* @param version protocol version of the frame
*/
sealed case class ProtocolFlags(
direction: MessageDirection,
version: ProtocolVersion
) {

/**
* Knowing the direction and version, we can provide a [[Codec]] for parsing the header. This codec starts by
* providing these flags and passes them to [[FrameHeader.withProtocol]] to parse the rest of [[FrameHeader]].
*/
lazy val headerCodec: Codec[FrameHeader] = version.headerCodec(direction)
}

object ProtocolFlags {
/**
* Represents the first byte in the frame, with the 1st bit representing the [[MessageDirection]] and the remaining
* 7 bits representing the [[ProtocolVersion]].
*/
implicit val codec: Codec[ProtocolFlags] = {
("direction" | MessageDirection.codec) ::
("version" | ProtocolVersion.codec)
}.as[ProtocolFlags]
}

/**
* Indicates what extra content is in the frame beyond the [[Message]].
* @param warning Whether or not warnings are present.
* @param customPayload Whether or not custom payload is present.
* @param tracing Whether or not tracing information is present.
* @param compression Whether or not the frame body is compressed.
*/
sealed case class HeaderFlags(
warning: Boolean = false,
customPayload: Boolean = false,
tracing: Boolean = false,
compression: Boolean = false
)

/**
* A version of [[HeaderFlags]] where all bits are disabled.
*/
object EmptyHeaderFlags extends HeaderFlags()

object HeaderFlags {
/**
* [[Codec]] for parsing [[HeaderFlags]] ignores first 4 bits and parses the remaining into the fields present on
* header flags.
*/
implicit val codec: Codec[HeaderFlags] = {
("reserved" | ignore(4)) ::
("warning" | bool) ::
("customPayload" | bool) ::
("tracing" | bool) ::
("compression" | bool)
}.dropUnits.as[HeaderFlags]
}

/**
* Represents the Frame Header of a native protocol [[Frame]].
* @param version The version and direction of the frame.
* @param flags header flags indicating additional content and whether or not the body is compressed.
* @param stream The stream id uniquely identifying a [[Frame]] in flight.
* @param opcode distinguishes the [[Message]] contained int he [[Frame]].
* @param length The length of the body of the [[Frame]].
*/
case class FrameHeader (
version: ProtocolFlags,
flags: HeaderFlags = EmptyHeaderFlags,
stream: Int = 0,
opcode: Int = ErrorMessage.opcode,
length: Long = 0
)

object FrameHeader {

private[codec] def withProtocol(protocol: ProtocolFlags) = {
("flags" | HeaderFlags.codec) ::
("stream" | protocol.version.streamIdCodec) ::
("opcode" | uint8) ::
("length" | uint32)
}

/**
* [[Codec]] that parses [[FrameHeader]] by first inspecting the [[ProtocolFlags]] and then uses the
* [[ProtocolVersion]] to determine how to parse the rest of the [[FrameHeader]].
*/
implicit val codec: Codec[FrameHeader] = {
ProtocolFlags.codec.consume { protocolFlags =>
protocolFlags.headerCodec
}(_.version)
}
}
Loading