diff options
Diffstat (limited to '')
231 files changed, 30394 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/java/CMakeLists.txt b/src/jaegertracing/thrift/lib/java/CMakeLists.txt new file mode 100644 index 000000000..a67845aba --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/CMakeLists.txt @@ -0,0 +1,94 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +if(ANDROID) + + set(THRIFT_AAR outputs/aar/thrift-debug.aar outputs/aar/thrift-release.aar) + add_custom_command( + OUTPUT ${THRIFT_AAR} + COMMAND ${GRADLE_EXECUTABLE} + -p "${CMAKE_CURRENT_SOURCE_DIR}/android" + "-PbuildDir=${CMAKE_CURRENT_BINARY_DIR}/android/build" assemble + ) + add_custom_target(thrift_aar ALL DEPENDS ${THRIFT_AAR}) + +else() + + if(IS_ABSOLUTE "${LIB_INSTALL_DIR}") + set(JAVA_INSTALL_DIR "${LIB_INSTALL_DIR}/java") + else() + set(JAVA_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/java") + endif() + + if(IS_ABSOLUTE "${DOC_INSTALL_DIR}") + set(JAVA_DOC_INSTALL_DIR "${DOC_INSTALL_DIR}/java") + else() + set(JAVA_DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${DOC_INSTALL_DIR}/java") + endif() + + set(PRELEASE "true") + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + set(PRELEASE "false") + endif () + + add_custom_target(ThriftJava ALL + COMMENT "Building Java library using Gradle Wrapper" + COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} assemble + --console=plain --no-daemon + -Prelease=${PRELEASE} + -Pthrift.version=${thrift_VERSION} + "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + # Enable publishing from CMake if the publishing information is provided + add_custom_target(MavenPublish + COMMENT "Publishing Java Library to Apache Maven staging" + COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} clean uploadArchives + --console=plain --no-daemon + -Prelease=${PRELEASE} + -Pthrift.version=${thrift_VERSION} + "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + + # Hook the CMake install process to the results from make ALL. + # This works best when 'make all && sudo make install/fast' is used. + # Using slash to end the source location to avoid copying the directory path. + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/libs/ + DESTINATION ${JAVA_INSTALL_DIR} + FILES_MATCHING PATTERN "libthrift-${thrift_VERSION}.jar") + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/deps/ + DESTINATION ${JAVA_INSTALL_DIR} + FILES_MATCHING PATTERN "*.jar") + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/build/docs/javadoc/ + DESTINATION ${JAVA_DOC_INSTALL_DIR}) + + if(BUILD_TESTING) + add_test(NAME JavaTest + COMMAND ${GRADLEW_EXECUTABLE} ${GRADLE_OPTS} test + --console=plain --no-daemon + -Prelease=${PRELEASE} + -Pthrift.version=${thrift_VERSION} + "-Pbuild.dir=${CMAKE_CURRENT_BINARY_DIR}/build" + "-Pthrift.compiler=${THRIFT_COMPILER}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + +endif() diff --git a/src/jaegertracing/thrift/lib/java/Makefile.am b/src/jaegertracing/thrift/lib/java/Makefile.am new file mode 100644 index 000000000..65981ca68 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/Makefile.am @@ -0,0 +1,72 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +export CLASSPATH + +all-local: + ./gradlew $(GRADLE_OPTS) assemble \ + -Prelease=true \ + -Pthrift.version=$(PACKAGE_VERSION) \ + --console=plain + +install-exec-hook: + ./gradlew $(GRADLE_OPTS) install \ + -Prelease=true \ + -Pinstall.path=$(DESTDIR)$(JAVA_PREFIX) \ + -Pinstall.javadoc.path=$(DESTDIR)$(docdir)/java \ + -Pthrift.version=$(PACKAGE_VERSION) \ + --console=plain + +clean-local: + ./gradlew $(GRADLE_OPTS) clean --console=plain + +precross: $(THRIFT) + ./gradlew $(GRADLE_OPTS) shadowJar \ + -Prelease=true \ + -Pthrift.version=$(PACKAGE_VERSION) \ + -Pthrift.compiler=$(THRIFT) \ + --console=plain + +check-local: $(THRIFT) + ./gradlew $(GRADLE_OPTS) test \ + -Prelease=true \ + -Pthrift.version=$(PACKAGE_VERSION) \ + -Pthrift.compiler=$(THRIFT) \ + --console=plain + +maven-publish: + ./gradlew $(GRADLE_OPTS) uploadArchives \ + -Prelease=true \ + -Pthrift.version=$(PACKAGE_VERSION) \ + --console=plain + +EXTRA_DIST = \ + build.gradle \ + gradle.properties \ + settings.gradle \ + gradle \ + gradlew \ + gradlew.bat \ + CMakeLists.txt \ + coding_standards.md \ + android \ + src \ + test \ + code_quality_tools \ + README.md diff --git a/src/jaegertracing/thrift/lib/java/README.md b/src/jaegertracing/thrift/lib/java/README.md new file mode 100644 index 000000000..c8ec1c37c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/README.md @@ -0,0 +1,188 @@ +Thrift Java Software Library + +License +======= + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. + +Building and installing from source +=================================== + +When using a CMake build from the source distribution on Linux the +easiest way to build and install is this simple command line: + + make all && sudo make install/fast + +It is important to use the install/fast option to eliminate +the automatic rebuild by dependency that causes issues because +the build tooling is designed to work with cached files in the +user home directory during the build process. Instead this builds +the code in the expected local build tree and then uses CMake +install code to copy to the target destination. + +Building Thrift with Gradle without CMake/Autoconf +================================================== + +The Thrift Java source is not build using the GNU tools, but rather uses +the Gradle build system, which tends to be predominant amongst Java +developers. + +To compile the Java Thrift libraries, simply do the following: + + ./gradlew + +Yep, that's easy. Look for libthrift-<version>.jar in the build/libs directory. + +The default build will run the unit tests which expect a usable +Thrift compiler to exist on the system. You have two choices for +that. + +* Build the Thrift executable from source at the default + location in the source tree. The project is configured + to look for it there. +* Install the published binary distribution to have Thrift + executable in a known location and add the path to the + ~/.gradle/gradle.properties file using the property name + "thrift.compiler". For example this would set the path in + a Windows box if Thrift was installed under C:\Thrift + + thrift.compiler=C:/Thrift/thrift.exe + +To just build the library without running unit tests you simply do this. + + ./gradlew assemble + +To install the library in the local Maven repository location +where other Maven or Gradle builds can reference it simply do this. + + ./gradlew install + +The library will be placed in your home directory under .m2/repository + +To include Thrift in your applications simply add libthrift.jar to your +classpath, or install if in your default system classpath of choice. + + +Build Thrift behind a proxy: + + ./gradlew -Dhttp.proxyHost=myproxyhost -Dhttp.proxyPort=8080 -Dhttp.proxyUser=thriftuser -Dhttp.proxyPassword=topsecret + +or via + + ./configure --with-java GRADLE_OPTS='-Dhttp.proxyHost=myproxyhost -Dhttp.proxyPort=8080 -Dhttp.proxyUser=thriftuser -Dhttp.proxyPassword=topsecret' + + +Unit Test HTML Reports +====================== + +The build will automatically generate an HTML Unit Test report. This can be found +under build/reports/tests/test/index.html. It can be viewed with a browser +directly from that location. + + +Clover Code Coverage for Thrift +=============================== + +The build will optionally generate Clover Code coverage if the Gradle property +`cloverEnabled=true` is set in ~/.gradle/gradle.properties or on the command line +via `-PcloverEnabled=true`. The generated report can be found under the location +build/reports/clover/html/index.html. It can be viewed with a browser +directly from that location. Additionally, a PDF report is generated and is found +under the location build/reports/clover/clover.pdf. + +The following command will build, unit test, and generate Clover reports: + + ./gradlew -PcloverEnabled=true + + +Publishing Maven Artifacts to Maven Central +=========================================== + +The Automake build generates a Makefile that provides the correct parameters +when you run the build provided the configure.ac has been set with the correct +version number. The Gradle build will receive the correct value for the build. +The same applies to the CMake build, the value from the configure.ac file will +be used if you execute these commands: + + make maven-publish -- This is for an Automake Linux build + make MavenPublish -- This is for a CMake generated build + +The uploadArchives task in Gradle is preconfigured with all necessary details +to sign and publish the artifacts from the build to the Apache Maven staging +repository. The task requires the following externally provided properties to +authenticate to the repository and sign the artifacts. The preferred approach +is to create or edit the ~/.gradle/gradle.properties file and add the following +properties to it. + + # Signing key information for artifacts PGP signature (values are examples) + signing.keyId=24875D73 + signing.password=secret + signing.secretKeyRingFile=/Users/me/.gnupg/secring.gpg + + # Apache Maven staging repository user credentials + mavenUser=meMyselfAndI + mavenPassword=MySuperAwesomeSecretPassword + +NOTE: If you do not have a secring.gpg file, see the +[gradle signing docs](https://docs.gradle.org/current/userguide/signing_plugin.html) +for instructions on how to generate it. + +It is also possible to manually publish using the Gradle build directly. +With the key information and credentials in place the following will generate +if needed the build artifacts and proceed to publish the results. + + ./gradlew -Prelease=true uploadArchives + +It is also possible to override the target repository for the Maven Publication +by using a Gradle property, for example you can publish signed JAR files to your +company internal server if you add this to the command line or in the +~/.gradle/gradle.properties file. The URL below assumes a Nexus Repository. + + maven-repository-url=https://my.company.com/service/local/staging/deploy/maven2 + +Or the same on the command line: + + ./gradlew -Pmaven-repository-url=https://my.company.com/service/local/staging/deploy/maven2 -Prelease=true -Pthrift.version=0.11.0 uploadArchives + + +Dependencies +============ + +Gradle +http://gradle.org/ + +# Breaking Changes + +## 0.13.0 + +* The signature of the 'process' method in TAsyncProcessor and TProcessor has +changed to remove the boolean return type and instead rely on Exceptions. + +* Per THRIFT-4805, TSaslTransportException has been removed. The same condition +is now covered by TTansportException, where `TTransportException.getType() == END_OF_FILE`. + +## 0.12.0 + +The access modifier of the AutoExpandingBuffer class has been changed from +public to default (package) and will no longer be accessible by third-party +libraries. + +The access modifier of the ShortStack class has been changed from +public to default (package) and will no longer be accessible by third-party +libraries. + diff --git a/src/jaegertracing/thrift/lib/java/android/build.gradle b/src/jaegertracing/thrift/lib/java/android/build.gradle new file mode 100644 index 000000000..c9984237b --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/android/build.gradle @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + useLibrary 'org.apache.http.legacy' + sourceSets.main.java { + srcDir '../src' + exclude 'org/apache/thrift/transport/TSaslClientTransport.java' + exclude 'org/apache/thrift/transport/TSaslServerTransport.java' + exclude 'org/apache/thrift/transport/TSaslTransport.java' + } +} + +repositories { + mavenCentral() +} +dependencies { + compile 'org.slf4j:slf4j-api:1.7.13' + compile 'javax.servlet:servlet-api:2.5' + compile 'org.apache.httpcomponents:httpcore:4.4.4' +} + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.5.0' + } +} diff --git a/src/jaegertracing/thrift/lib/java/android/settings.gradle b/src/jaegertracing/thrift/lib/java/android/settings.gradle new file mode 100644 index 000000000..75e97be41 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name='thrift' diff --git a/src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml b/src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..43abdb758 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest package="org.apache.thrift"> + <application /> +</manifest> diff --git a/src/jaegertracing/thrift/lib/java/build.gradle b/src/jaegertracing/thrift/lib/java/build.gradle new file mode 100644 index 000000000..5f0d2782b --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/build.gradle @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Using the legacy plugin classpath for Clover so it can be loaded optionally +buildscript { + repositories { + mavenCentral() + google() + jcenter() + gradlePluginPortal() + } + + dependencies { + classpath 'com.bmuschko:gradle-clover-plugin:2.2.1' + } +} + +plugins { + id 'java' + id 'maven' + id 'signing' + id 'com.github.johnrengelman.shadow' version '4.0.4' +} + +description = 'Apache Thrift Java Library' + +defaultTasks 'build' + +// Version components for this project +group = property('thrift.groupid') + +if (Boolean.parseBoolean(project.release)) { + version = property('thrift.version') +} else { + version = property('thrift.version') + '-SNAPSHOT' +} + +// Keeping the rest of the build logic in functional named scripts for clarity +apply from: 'gradle/environment.gradle' +apply from: 'gradle/sourceConfiguration.gradle' +apply from: 'gradle/additionalArtifacts.gradle' +apply from: 'gradle/generateTestThrift.gradle' +apply from: 'gradle/unitTests.gradle' +apply from: 'gradle/cloverCoverage.gradle' +apply from: 'gradle/functionalTests.gradle' +apply from: 'gradle/publishing.gradle' +apply from: 'gradle/codeQualityChecks.gradle' diff --git a/src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml b/src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml new file mode 100644 index 000000000..8a93b0ad0 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/code_quality_tools/findbugs-filter.xml @@ -0,0 +1,51 @@ +<FindBugsFilter> + <!-- + This file controls filtering some of the more obnoxious findbugs reports. + Some may be worthy of examination and resolution, others are too nit-picky. + --> + <Match> + <Or> + <!-- Filter the missing serialVersionUID errors --> + <Bug code="SnVI" /> + <!-- Filter Malicious code vulnerability Warnings --> + <Bug code="EI,EI2" /> + <!-- Filter Unchecked/unconfirmed cast --> + <Bug code="BC" /> + <!-- Filter Should return a zero length array rather than null? --> + <Bug code="PZLA" /> + <!-- Filter Redundant nullcheck --> + <Bug code="RCN" /> + <!-- Filter Exception is caught when Exception is not thrown --> + <Bug code="REC" /> + <!-- Filter Switch statement found where default case is missing --> + <Bug code="SF" /> + <!-- Filter Unread public/protected field --> + <Bug code="UrF" /> + <!-- Filter Field not initialized in constructor and dereferenced --> + <Bug code="UwF" /> + </Or> + </Match> + <Match> + <!-- Filter method invokes System.exit(...), which shuts down the entire virtual machine --> + <Class name="org.apache.thrift.transport.TFileTransport" /> + <Method name="printUsage" /> + <Bug code="Dm" /> + </Match> + <Match> + <!-- Filter method might ignore java.lang.Exception --> + <Class name="org.apache.thrift.transport.TSimpleFileTransport" /> + <Method name="close" /> + <Bug code="DE" /> + </Match> + <Match> + <!-- Filter method might ignore java.lang.Exception --> + <Class name="org.apache.thrift.TNonblockingMultiFetchClient$MultiFetch" /> + <Method name="run" /> + <Bug code="DE" /> + </Match> + <Match> + <!-- Filter Class defines non-transient non-serializable instance field --> + <Class name="org.apache.thrift.server.TServlet" /> + <Bug code="Se" /> + </Match> +</FindBugsFilter> diff --git a/src/jaegertracing/thrift/lib/java/coding_standards.md b/src/jaegertracing/thrift/lib/java/coding_standards.md new file mode 100644 index 000000000..fa0390bb5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/src/jaegertracing/thrift/lib/java/gradle.properties b/src/jaegertracing/thrift/lib/java/gradle.properties new file mode 100644 index 000000000..081165910 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle.properties @@ -0,0 +1,34 @@ +# This file is shared currently between this Gradle build and the +# Ant builds for fd303 and JavaScript. Keep the dotted notation for +# the properties to minimize the changes in the dependencies. +thrift.version=0.13.0 +thrift.groupid=org.apache.thrift +release=false + +# Local Install paths +install.path=/usr/local/lib +install.javadoc.path=/usr/local/lib + +# Test execution properties +testPort=9090 + +# Test with Clover Code coverage (disabled by default) +cloverEnabled=false + +# Maven dependency download locations +mvn.repo=http://repo1.maven.org/maven2 +apache.repo=https://repository.apache.org/content/repositories/releases + +# Apache Maven publish +license=http://www.apache.org/licenses/LICENSE-2.0.txt +maven-repository-url=https://repository.apache.org/service/local/staging/deploy/maven2 +maven-repository-id=apache.releases.https + +# Dependency versions +httpclient.version=4.5.6 +httpcore.version=4.4.1 +slf4j.version=1.7.25 +servlet.version=2.5 +junit.version=4.12 +mockito.version=1.9.5 +javax.annotation.version=1.3.2 diff --git a/src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle b/src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle new file mode 100644 index 000000000..201469da1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/additionalArtifacts.gradle @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +task sourcesJar(type: Jar, group: 'Build') { + description = 'Assembles a jar archive containing the main Java sources.' + + classifier 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc, group: 'Build') { + description = 'Assembles a jar archive containing the JavaDoc.' + + classifier 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + diff --git a/src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle b/src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle new file mode 100644 index 000000000..cef0e79b1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/cloverCoverage.gradle @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// Keep this as an optional feature for now, disabled by default +if (Boolean.parseBoolean(project.cloverEnabled)) { + apply plugin: 'com.bmuschko.clover' + + dependencies { + clover 'org.openclover:clover:4.2.+' + } + + clover { + + testIncludes = ['**/Test*.java'] + // Exclude the generated test code from code coverage + testExcludes = ['thrift/test/Test*.java'] + + compiler { + encoding = 'UTF-8' + debug = true + } + + report { + html = true + pdf = true + } + } + + build.dependsOn cloverGenerateReport +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle b/src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle new file mode 100644 index 000000000..1ff1c297d --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/codeQualityChecks.gradle @@ -0,0 +1,39 @@ + +// ================================================================= +// Configure the Gradle code quality plugins here. +// + +apply plugin: 'findbugs' + +findbugs { + ignoreFailures = true + toolVersion = '3.0.1' + sourceSets = [ sourceSets.main ] + effort = 'max' + reportLevel = 'low' + excludeFilter = file('code_quality_tools/findbugs-filter.xml') +} + +tasks.withType(FindBugs) { + reports { + text.enabled = false + html.enabled = true + xml.enabled = false + } +} + +apply plugin: 'pmd' + +pmd { + ignoreFailures = true + toolVersion = '6.0.0' + sourceSets = [ sourceSets.main ] + ruleSets = [ 'java-basic' ] +} + +tasks.withType(Pmd) { + reports { + html.enabled = true + xml.enabled = false + } +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/environment.gradle b/src/jaegertracing/thrift/lib/java/gradle/environment.gradle new file mode 100644 index 000000000..45fa63a17 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/environment.gradle @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// Override the build directory if CMake is used (allows for out-of-tree-builds) +if (hasProperty('build.dir')) { + buildDir = file(property('build.dir')) +} + +// In order to remain compatible with other Ant based builds in the system +// we convert the gradle.properties into DSL friendly camelCased properties +ext.installPath = property('install.path') +ext.installJavadocPath = property('install.javadoc.path') + +ext.thriftRoot = file('../..') + +if (hasProperty('thrift.compiler')) { + ext.thriftCompiler = property('thrift.compiler') +} else { + ext.thriftCompiler = "$thriftRoot/compiler/cpp/thrift" +} + +ext.mvnRepo = property('mvn.repo') +ext.apacheRepo = property('apache.repo') +ext.mavenRepositoryUrl = property('maven-repository-url') + +// Versions used in this project +ext.httpclientVersion = property('httpclient.version') +ext.httpcoreVersion = property('httpcore.version') +ext.servletVersion = property('servlet.version') +ext.slf4jVersion = property('slf4j.version') +ext.junitVersion = property('junit.version') +ext.mockitoVersion = property('mockito.version') +ext.javaxAnnotationVersion = property('javax.annotation.version') + +// In this section you declare where to find the dependencies of your project +repositories { + maven { + name 'Maven Central Repository' + url mvnRepo + } + maven { + name 'Apache Maven Repository' + url apacheRepo + } +} + +dependencies { + compile "org.slf4j:slf4j-api:${slf4jVersion}" + compile "org.apache.httpcomponents:httpclient:${httpclientVersion}" + compile "org.apache.httpcomponents:httpcore:${httpcoreVersion}" + compile "javax.servlet:servlet-api:${servletVersion}" + compile "javax.annotation:javax.annotation-api:${javaxAnnotationVersion}" + + testCompile "junit:junit:${junitVersion}" + testCompile "org.mockito:mockito-all:${mockitoVersion}" + testRuntime "org.slf4j:slf4j-log4j12:${slf4jVersion}" +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle b/src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle new file mode 100644 index 000000000..c420d122c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/functionalTests.gradle @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// ---------------------------------------------------------------------------- +// Functional testing harness creation. This helps run the cross-check tests. +// The Makefile precross target invokes the shadowJar task and the tests.json +// code is changed to call runclient or runserver as needed. + +// ---------------------------------------------------------------------------- +// Cross Test sources are separated in their own sourceSet +// +sourceSets { + crossTest { + java { + srcDir 'test' + include '**/test/TestClient.java' + include '**/test/TestServer.java' + include '**/test/TestNonblockingServer.java' + } + } +} + +configurations { + crossTestCompile { extendsFrom testCompile } + crossTestRuntime { extendsFrom crossTestCompile, testRuntime } +} + +dependencies { + crossTestCompile sourceSets.main.output + crossTestCompile sourceSets.test.output +} + +// I am using shadow plugin to make a self contained functional test Uber JAR that +// eliminates startup problems with wrapping the cross-check harness in Gradle. +// This is used by the runner scripts as the single classpath entry which +// allows the process to be as lightweight as it can. +shadowJar { + description = 'Assemble a test JAR file for cross-check execution' + // make sure the runners are created when this runs + dependsOn 'generateRunnerScriptForClient', 'generateRunnerScriptForServer', 'generateRunnerScriptForNonblockingServer' + + baseName = 'functionalTest' + destinationDir = file("$buildDir/functionalTestJar") + classifier = null + + // We do not need a version number for this internal jar + version = null + + // Bundle the complete set of unit test classes including generated code + // and the runtime dependencies in one JAR to expedite execution. + from sourceSets.test.output + from sourceSets.crossTest.output + configurations = [project.configurations.testRuntime] +} + +// Common script runner configuration elements +def scriptExt = '' +def execExt = '' +def scriptHead = '#!/bin/bash' +def args = '$*' + +// Although this is marked internal it is an available and stable interface +if (org.gradle.internal.os.OperatingSystem.current().windows) { + scriptExt = '.bat' + execExt = '.exe' + scriptHead = '@echo off' + args = '%*' +} + +// The Java executable to use with the runner scripts +def javaExe = file("${System.getProperty('java.home')}/bin/java${execExt}").canonicalPath +// The common Uber jar path +def jarPath = shadowJar.archivePath.canonicalPath +def trustStore = file('test/.truststore').canonicalPath +def keyStore = file('test/.keystore').canonicalPath + +task generateRunnerScriptForClient(group: 'Build') { + description = 'Generate a runner script for cross-check tests with TestClient' + + def clientFile = file("$buildDir/runclient${scriptExt}") + + def runClientText = """\ +${scriptHead} + +"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.trustStore=$trustStore" -Djavax.net.ssl.trustStorePassword=thrift org.apache.thrift.test.TestClient $args +""" + inputs.property 'runClientText', runClientText + outputs.file clientFile + + doLast { + clientFile.parentFile.mkdirs() + clientFile.text = runClientText + clientFile.setExecutable(true, false) + } +} + +task generateRunnerScriptForServer(group: 'Build') { + description = 'Generate a runner script for cross-check tests with TestServer' + + def serverFile = file("$buildDir/runserver${scriptExt}") + + def runServerText = """\ +${scriptHead} + +"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$keyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestServer $args +""" + + inputs.property 'runServerText', runServerText + outputs.file serverFile + + doLast { + serverFile.parentFile.mkdirs() + serverFile.text = runServerText + serverFile.setExecutable(true, false) + } +} + +task generateRunnerScriptForNonblockingServer(group: 'Build') { + description = 'Generate a runner script for cross-check tests with TestNonblockingServer' + + def serverFile = file("$buildDir/runnonblockingserver${scriptExt}") + + def runServerText = """\ +${scriptHead} + +"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$keyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestNonblockingServer $args +""" + + inputs.property 'runServerText', runServerText + outputs.file serverFile + + doLast { + serverFile.parentFile.mkdirs() + serverFile.text = runServerText + serverFile.setExecutable(true, false) + } +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle b/src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle new file mode 100644 index 000000000..121bf537d --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/generateTestThrift.gradle @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// Generated code locations for Unit tests +ext.genSrc = file("$buildDir/gen-java") +ext.genBeanSrc = file("$buildDir/gen-javabean") +ext.genReuseSrc = file("$buildDir/gen-javareuse") +ext.genFullCamelSrc = file("$buildDir/gen-fullcamel") +ext.genUnsafeSrc = file("$buildDir/gen-unsafe") + +// Add the generated code directories to the test source set +sourceSets { + test.java.srcDirs genSrc, genBeanSrc, genReuseSrc, genFullCamelSrc, genUnsafeSrc +} + +// ---------------------------------------------------------------------------- +// Code generation for Unit Testing + +// A callable closure to make this easier +ext.thriftCompile = { Task task, String thriftFileName, String generator = 'java', File outputDir = genSrc -> + def thriftFile = file("$thriftRoot/test/$thriftFileName") + assert thriftFile.exists() + + task.inputs.file thriftFile + task.outputs.dir outputDir + + task.doLast { + outputDir.mkdirs() + def result = exec { + executable file(thriftCompiler) + args '--gen', generator + args '-out', outputDir + args thriftFile + standardOutput = task.outputBuffer + errorOutput = task.outputBuffer + ignoreExitValue = true + } + if (result.exitValue != 0) { + // Only show the Thrift compiler output on failures, cuts down on noise! + println task.outputBuffer.toString() + result.rethrowFailure() + } + } +} + +task generate(group: 'Build') { + description = 'Generate all unit test Thrift sources' + compileTestJava.dependsOn it +} + +task generateJava(group: 'Build') { + description = 'Generate the thrift gen-java source' + generate.dependsOn it + + ext.outputBuffer = new ByteArrayOutputStream() + + thriftCompile(it, 'ThriftTest.thrift') + thriftCompile(it, 'JavaTypes.thrift') + thriftCompile(it, 'DebugProtoTest.thrift') + thriftCompile(it, 'DoubleConstantsTest.thrift') + thriftCompile(it, 'OptionalRequiredTest.thrift') + thriftCompile(it, 'ManyOptionals.thrift') + thriftCompile(it, 'JavaDeepCopyTest.thrift') + thriftCompile(it, 'EnumContainersTest.thrift') + thriftCompile(it, 'JavaBinaryDefault.thrift') +} + +task generateBeanJava(group: 'Build') { + description = 'Generate the thrift gen-javabean source' + generate.dependsOn it + + ext.outputBuffer = new ByteArrayOutputStream() + + thriftCompile(it, 'JavaBeansTest.thrift', 'java:beans,nocamel', genBeanSrc) +} + +task generateReuseJava(group: 'Build') { + description = 'Generate the thrift gen-javareuse source' + generate.dependsOn it + + ext.outputBuffer = new ByteArrayOutputStream() + + thriftCompile(it, 'FullCamelTest.thrift', 'java:fullcamel', genFullCamelSrc) +} + +task generateFullCamelJava(group: 'Build') { + description = 'Generate the thrift gen-fullcamel source' + generate.dependsOn it + + ext.outputBuffer = new ByteArrayOutputStream() + + thriftCompile(it, 'ReuseObjects.thrift', 'java:reuse-objects', genReuseSrc) +} + +task generateUnsafeBinariesJava(group: 'Build') { + description = 'Generate the thrift gen-unsafebinaries source' + generate.dependsOn it + + ext.outputBuffer = new ByteArrayOutputStream() + + thriftCompile(it, 'UnsafeTypes.thrift', 'java:unsafe_binaries', genUnsafeSrc) +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/publishing.gradle b/src/jaegertracing/thrift/lib/java/gradle/publishing.gradle new file mode 100644 index 000000000..029bff93d --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/publishing.gradle @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// ---------------------------------------------------------------------------- +// Installation subtasks, not used currently, we use "make install/fast" +task installDist(type: Copy, group: 'Install') { + description = "Copy Thrift JAR and dependencies into $installPath location" + + destinationDir = file(installPath) + + from jar + from configurations.compile +} + +task installJavadoc(type: Copy, group: 'Install', dependsOn: javadoc) { + description = "Install Thrift JavaDoc into $installJavadocPath location" + + destinationDir = file(installJavadocPath) + + from javadoc.destinationDir +} + +// This is not needed by Gradle builds but the remaining Ant builds seem to +// need access to the generated test classes for Thrift unit tests so we +// assist them to use it this way. +task copyDependencies(type: Copy, group: 'Build') { + description = 'Copy runtime dependencies in a common location for other Ant based projects' + project.assemble.dependsOn it + + destinationDir = file("$buildDir/deps") + from configurations.testRuntime + // exclude some very specific unit test dependencies + exclude '**/junit*.jar', '**/mockito*.jar', '**/hamcrest*.jar' +} + +// ---------------------------------------------------------------------------- +// Allow this configuration to be shared between install and uploadArchives tasks +def configurePom(pom) { + pom.project { + name 'Apache Thrift' + description 'Thrift is a software framework for scalable cross-language services development.' + packaging 'jar' + url 'http://thrift.apache.org' + + scm { + url 'https://github.com/apache/thrift' + connection 'scm:git:https://github.com/apache/thrift.git' + developerConnection 'scm:git:git@github.com:apache/thrift.git' + } + + licenses { + license { + name 'The Apache Software License, Version 2.0' + url "${project.license}" + distribution 'repo' + } + } + + developers { + developer { + id 'dev' + name 'Apache Thrift Developers' + email 'dev@thrift.apache.org' + } + } + } + + pom.whenConfigured { + // Fixup the scope for servlet-api to be 'provided' instead of 'compile' + dependencies.find { dep -> dep.groupId == 'javax.servlet' && dep.artifactId == 'servlet-api' }.with { + // it.optional = true + it.scope = 'provided' + } + } +} + +install { + repositories.mavenInstaller { + configurePom(pom) + } +} + +uploadArchives { + dependsOn test // make sure we run unit tests when publishing + repositories.mavenDeployer { + // signPom will silently do nothing when no signing information is provided + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + repository(url: project.mavenRepositoryUrl) { + if (project.hasProperty('mavenUser') && project.hasProperty('mavenPassword')) { + authentication(userName: mavenUser, password: mavenPassword) + } + } + configurePom(pom) + } +} + +// Signing configuration, optional, only when release and uploadArchives is activated +signing { + required { !version.endsWith("SNAPSHOT") && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle b/src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle new file mode 100644 index 000000000..8dd0331f7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/sourceConfiguration.gradle @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// ---------------------------------------------------------------------------- +// source sets for main and test sources +sourceSets { + main { + java { + srcDir 'src' + } + } + test { + java { + srcDir 'test' + // see functionalTests.gradle for these files + exclude '**/test/TestClient.java' + exclude '**/test/TestServer.java' + exclude '**/test/TestNonblockingServer.java' + } + resources { + srcDir 'test' + include 'log4j.properties' + } + } +} + +// ---------------------------------------------------------------------------- +// Compiler configuration details + +sourceCompatibility = '1.8' +targetCompatibility = '1.8' + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' + options.debug = true + options.deprecation = true + // options.compilerArgs.addAll('-Xlint:unchecked') +} + +// ---------------------------------------------------------------------------- +// Jar packaging details +processResources { + into('META-INF') { + from "$thriftRoot/LICENSE" + from "$thriftRoot/NOTICE" + rename('(.+)', '$1.txt') + } +} + +jar { + project.test.dependsOn it + manifest { + attributes([ + "Implementation-Version": "${project.version}", + "Bundle-ManifestVersion": "2", + "Bundle-SymbolicName": "${project.group}", + "Bundle-Name": "Apache Thrift", + "Bundle-Version": "${project.version}", + "Bundle-Description": "Apache Thrift library", + "Bundle-License": "${project.license}", + "Bundle-ActivationPolicy": "lazy", + "Export-Package": "${project.group}.async;uses:=\"${project.group}.protocol,${project.group}.transport,org.slf4j,${project.group}\";version=\"${version}\",${project.group}.protocol;uses:=\"${project.group}.transport,${project.group},${project.group}.scheme\";version=\"${version}\",${project.group}.server;uses:=\"${project.group}.transport,${project.group}.protocol,${project.group},org.slf4j,javax.servlet,javax.servlet.http\";version=\"${version}\",${project.group}.transport;uses:=\"${project.group}.protocol,${project.group},org.apache.http.client,org.apache.http.params,org.apache.http.entity,org.apache.http.client.methods,org.apache.http,org.slf4j,javax.net.ssl,javax.net,javax.security.sasl,javax.security.auth.callback\";version=\"${version}\",${project.group};uses:=\"${project.group}.protocol,${project.group}.async,${project.group}.server,${project.group}.transport,org.slf4j,org.apache.log4j,${project.group}.scheme\";version=\"${version}\",${project.group}.meta_data;uses:=\"${project.group}\";version=\"${version}\",${project.group}.scheme;uses:=\"${project.group}.protocol,${project.group}\";version=\"${version}\"", + "Import-Package": "javax.net,javax.net.ssl,javax.security.auth.callback,javax.security.sasl,javax.servlet;resolution:=optional,javax.servlet.http;resolution:=optional,org.slf4j;resolution:=optional;version=\"[1.4,2)\",org.apache.http.client;resolution:=optional,org.apache.http.params;resolution:=optional,org.apache.http.entity;resolution:=optional,org.apache.http.client.methods;resolution:=optional,org.apache.http;resolution:=optional" + ]) + } +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle b/src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle new file mode 100644 index 000000000..61f2fbdeb --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/unitTests.gradle @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +// Following Gradle best practices to keep build logic organized + +// Bundle the test classes in a JAR for other Ant based builds +task testJar(type: Jar, group: 'Build') { + description = 'Assembles a jar archive containing the test classes.' + project.test.dependsOn it + + classifier 'test' + from sourceSets.test.output +} + +// ---------------------------------------------------------------------------- +// Unit test tasks and configurations + +// Help the up to date algorithm to make these tests done +ext.markTaskDone = { task -> + def buildFile = file("$buildDir/${task.name}.flag") + task.inputs.files task.classpath + task.outputs.file buildFile + task.doLast { + buildFile.text = 'Passed!' + } +} + +task deprecatedEqualityTest(type: JavaExec, group: 'Verification') { + description = 'Run the non-JUnit test suite ' + classpath = sourceSets.test.runtimeClasspath + main 'org.apache.thrift.test.EqualityTest' + markTaskDone(it) +} + +task deprecatedJavaBeansTest(type: JavaExec, group: 'Verification') { + description = 'Run the non-JUnit test suite ' + classpath = sourceSets.test.runtimeClasspath + main 'org.apache.thrift.test.JavaBeansTest' + markTaskDone(it) +} + +// Main Unit Test task configuration +test { + description="Run the full test suite" + dependsOn deprecatedEqualityTest, deprecatedJavaBeansTest + + // Allow repeating tests even after successful execution + if (project.hasProperty('rerunTests')) { + outputs.upToDateWhen { false } + } + + include '**/Test*.class' + exclude '**/Test*\$*.class' + + maxHeapSize = '512m' + forkEvery = 1 + + systemProperties = [ + 'build.test': "${compileTestJava.destinationDir}", + 'test.port': "${testPort}", + 'javax.net.ssl.trustStore': "${projectDir}/test/.truststore", + 'javax.net.ssl.trustStorePassword': 'thrift', + 'javax.net.ssl.keyStore': "${projectDir}/test/.keystore", + 'javax.net.ssl.keyStorePassword': 'thrift' + ] +} diff --git a/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jar b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 000000000..5c2d1cf01 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.jar diff --git a/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..7c4388a92 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/src/jaegertracing/thrift/lib/java/gradlew b/src/jaegertracing/thrift/lib/java/gradlew new file mode 100755 index 000000000..83f2acfdc --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradlew @@ -0,0 +1,188 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# 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 +# +# https://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. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/src/jaegertracing/thrift/lib/java/gradlew.bat b/src/jaegertracing/thrift/lib/java/gradlew.bat new file mode 100644 index 000000000..9618d8d96 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/gradlew.bat @@ -0,0 +1,100 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/jaegertracing/thrift/lib/java/settings.gradle b/src/jaegertracing/thrift/lib/java/settings.gradle new file mode 100644 index 000000000..c9bd8bc0e --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/settings.gradle @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +rootProject.name = 'libthrift' diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java new file mode 100644 index 000000000..483c8d054 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/AsyncProcessFunction.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.server.AbstractNonblockingServer; + +public abstract class AsyncProcessFunction<I, T extends TBase, R> { + final String methodName; + + public AsyncProcessFunction(String methodName) { + this.methodName = methodName; + } + + protected abstract boolean isOneway(); + + public abstract void start(I iface, T args, AsyncMethodCallback<R> resultHandler) throws TException; + + public abstract T getEmptyArgsInstance(); + + public abstract AsyncMethodCallback<R> getResultHandler(final AbstractNonblockingServer.AsyncFrameBuffer fb, int seqid); + + public String getMethodName() { + return methodName; + } + + public void sendResponse(final AbstractNonblockingServer.AsyncFrameBuffer fb, final TSerializable result, final byte type, final int seqid) throws TException { + TProtocol oprot = fb.getOutputProtocol(); + + oprot.writeMessageBegin(new TMessage(getMethodName(), type, seqid)); + result.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + + fb.responseReady(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java new file mode 100644 index 000000000..bf14ef57e --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/EncodingUtils.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Utility methods for use when encoding/decoding raw data as byte arrays. + */ +public class EncodingUtils { + + /** + * Encode <code>integer</code> as a series of 4 bytes into <code>buf</code> + * starting at position 0 within that buffer. + * + * @param integer + * The integer to encode. + * @param buf + * The buffer to write to. + */ + public static final void encodeBigEndian(final int integer, final byte[] buf) { + encodeBigEndian(integer, buf, 0); + } + + /** + * Encode <code>integer</code> as a series of 4 bytes into <code>buf</code> + * starting at position <code>offset</code>. + * + * @param integer + * The integer to encode. + * @param buf + * The buffer to write to. + * @param offset + * The offset within <code>buf</code> to start the encoding. + */ + public static final void encodeBigEndian(final int integer, final byte[] buf, int offset) { + buf[offset] = (byte) (0xff & (integer >> 24)); + buf[offset + 1] = (byte) (0xff & (integer >> 16)); + buf[offset + 2] = (byte) (0xff & (integer >> 8)); + buf[offset + 3] = (byte) (0xff & (integer)); + } + + /** + * Decode a series of 4 bytes from <code>buf</code>, starting at position 0, + * and interpret them as an integer. + * + * @param buf + * The buffer to read from. + * @return An integer, as read from the buffer. + */ + public static final int decodeBigEndian(final byte[] buf) { + return decodeBigEndian(buf, 0); + } + + /** + * Decode a series of 4 bytes from <code>buf</code>, start at + * <code>offset</code>, and interpret them as an integer. + * + * @param buf + * The buffer to read from. + * @param offset + * The offset with <code>buf</code> to start the decoding. + * @return An integer, as read from the buffer. + */ + public static final int decodeBigEndian(final byte[] buf, int offset) { + return ((buf[offset] & 0xff) << 24) | ((buf[offset + 1] & 0xff) << 16) + | ((buf[offset + 2] & 0xff) << 8) | ((buf[offset + 3] & 0xff)); + } + + /** + * Bitfield utilities. + * Returns true if the bit at position is set in v. + */ + public static final boolean testBit(byte v, int position) { + return testBit((int)v, position); + } + + public static final boolean testBit(short v, int position) { + return testBit((int)v, position); + } + + public static final boolean testBit(int v, int position) { + return (v & (1 << position)) != 0; + } + + public static final boolean testBit(long v, int position) { + return (v & (1L << position)) != 0L; + } + + /** + * Returns v, with the bit at position set to zero. + */ + public static final byte clearBit(byte v, int position) { + return (byte)clearBit((int)v, position); + } + + public static final short clearBit(short v, int position) { + return (short)clearBit((int)v, position); + } + + public static final int clearBit(int v, int position) { + return v & ~(1 << position); + } + + public static final long clearBit(long v, int position) { + return v & ~(1L << position); + } + + /** + * Returns v, with the bit at position set to 1 or 0 depending on value. + */ + public static final byte setBit(byte v, int position, boolean value) { + return (byte)setBit((int)v, position, value); + } + + public static final short setBit(short v, int position, boolean value) { + return (short)setBit((int)v, position, value); + } + + public static final int setBit(int v, int position, boolean value) { + if(value) + return v | (1 << position); + else + return clearBit(v, position); + } + + public static final long setBit(long v, int position, boolean value) { + if(value) + return v | (1L << position); + else + return clearBit(v, position); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java new file mode 100644 index 000000000..d5cd309e8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/Option.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Implementation of the Option type pattern + */ +public abstract class Option<T> { + + @SuppressWarnings("rawtypes") + private static final Option NONE = new None(); + + /** + * Whether the Option is defined or not + * @return + * true if the Option is defined (of type Some) + * false if the Option is not defined (of type None) + */ + public abstract boolean isDefined(); + + /** + * Get the value of the Option (if it is defined) + * @return the value + * @throws IllegalStateException if called on a None + */ + public abstract T get(); + + /** + * Get the contained value (if defined) or else return a default value + * @param other what to return if the value is not defined (a None) + * @return either the value, or other if the value is not defined + */ + public T or(T other) { + if (isDefined()) { + return get(); + } else { + return other; + } + } + /** + * The None type, representing an absent value (instead of "null") + */ + public static class None<T> extends Option<T> { + public boolean isDefined() { + return false; + } + + public T get() { + throw new IllegalStateException("Cannot call get() on None"); + } + + public String toString() { + return "None"; + } + } + + /** + * The Some type, representing an existence of some value + * @param <T> The type of value + */ + public static class Some<T> extends Option<T> { + private final T value; + public Some(T value) { + this.value = value; + } + + public boolean isDefined() { + return true; + } + + public T get() { + return value; + } + + public String toString() { + return "Some(" + value + ")"; + } + } + + /** + * Wraps value in an Option type, depending on whether or not value is null + * @param value + * @param <T> type of value + * @return Some(value) if value is not null, None if value is null + */ + public static <T> Option<T> fromNullable(T value) { + if (value != null) { + return some(value); + } else { + return none(); + } + } + + /** + * Wrap value in a Some type (NB! value must not be null!) + * @param value + * @param <T> type of value + * @return a new Some(value) + */ + public static <T> Some<T> some(T value) { + return new Some<T>(value); + } + + @SuppressWarnings("unchecked") + public static <T> None<T> none() { + return (None<T>) NONE; + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java new file mode 100644 index 000000000..e6213dfa1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/ProcessFunction.java @@ -0,0 +1,88 @@ +package org.apache.thrift; + +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolException; +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class ProcessFunction<I, T extends TBase> { + private final String methodName; + + private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFunction.class.getName()); + + public ProcessFunction(String methodName) { + this.methodName = methodName; + } + + public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) throws TException { + T args = getEmptyArgsInstance(); + try { + args.read(iprot); + } catch (TProtocolException e) { + iprot.readMessageEnd(); + TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage()); + oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid)); + x.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + return; + } + iprot.readMessageEnd(); + TSerializable result = null; + byte msgType = TMessageType.REPLY; + + try { + result = getResult(iface, args); + } catch (TTransportException ex) { + LOGGER.error("Transport error while processing " + getMethodName(), ex); + throw ex; + } catch (TApplicationException ex) { + LOGGER.error("Internal application error processing " + getMethodName(), ex); + result = ex; + msgType = TMessageType.EXCEPTION; + } catch (Exception ex) { + LOGGER.error("Internal error processing " + getMethodName(), ex); + if(rethrowUnhandledExceptions()) throw new RuntimeException(ex.getMessage(), ex); + if(!isOneway()) { + result = new TApplicationException(TApplicationException.INTERNAL_ERROR, + "Internal error processing " + getMethodName()); + msgType = TMessageType.EXCEPTION; + } + } + + if(!isOneway()) { + oprot.writeMessageBegin(new TMessage(getMethodName(), msgType, seqid)); + result.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + } + } + + private void handleException(int seqid, TProtocol oprot) throws TException { + if (!isOneway()) { + TApplicationException x = new TApplicationException(TApplicationException.INTERNAL_ERROR, + "Internal error processing " + getMethodName()); + oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.EXCEPTION, seqid)); + x.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + } + } + + protected boolean rethrowUnhandledExceptions(){ + return false; + } + + protected abstract boolean isOneway(); + + public abstract TBase getResult(I iface, T args) throws TException; + + public abstract T getEmptyArgsInstance(); + + public String getMethodName() { + return methodName; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java new file mode 100644 index 000000000..4d693d9ce --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TApplicationException.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolUtil; +import org.apache.thrift.protocol.TStruct; +import org.apache.thrift.protocol.TType; + +/** + * Application level exception + * + */ +public class TApplicationException extends TException implements TSerializable { + + private static final TStruct TAPPLICATION_EXCEPTION_STRUCT = new TStruct("TApplicationException"); + private static final TField MESSAGE_FIELD = new TField("message", TType.STRING, (short)1); + private static final TField TYPE_FIELD = new TField("type", TType.I32, (short)2); + + private static final long serialVersionUID = 1L; + + public static final int UNKNOWN = 0; + public static final int UNKNOWN_METHOD = 1; + public static final int INVALID_MESSAGE_TYPE = 2; + public static final int WRONG_METHOD_NAME = 3; + public static final int BAD_SEQUENCE_ID = 4; + public static final int MISSING_RESULT = 5; + public static final int INTERNAL_ERROR = 6; + public static final int PROTOCOL_ERROR = 7; + public static final int INVALID_TRANSFORM = 8; + public static final int INVALID_PROTOCOL = 9; + public static final int UNSUPPORTED_CLIENT_TYPE = 10; + + protected int type_ = UNKNOWN; + private String message_ = null; + + public TApplicationException() { + super(); + } + + public TApplicationException(int type) { + super(); + type_ = type; + } + + public TApplicationException(int type, String message) { + super(message); + type_ = type; + } + + public TApplicationException(String message) { + super(message); + } + + public int getType() { + return type_; + } + + @Override + public String getMessage() { + if (message_ == null) { + return super.getMessage(); + } + else { + return message_; + } + } + + public void read(TProtocol iprot) throws TException + { + TField field; + iprot.readStructBegin(); + + String message = null; + int type = UNKNOWN; + + while (true) { + field = iprot.readFieldBegin(); + if (field.type == TType.STOP) { + break; + } + switch (field.id) { + case 1: + if (field.type == TType.STRING) { + message = iprot.readString(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case 2: + if (field.type == TType.I32) { + type = iprot.readI32(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + default: + TProtocolUtil.skip(iprot, field.type); + break; + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + type_ = type; + message_ = message; + } + + /** + * Convenience factory method for constructing a TApplicationException given a TProtocol input + */ + public static TApplicationException readFrom(TProtocol iprot) throws TException + { + TApplicationException result = new TApplicationException(); + result.read(iprot); + return result; + } + + public void write(TProtocol oprot) throws TException + { + oprot.writeStructBegin(TAPPLICATION_EXCEPTION_STRUCT); + if (getMessage() != null) { + oprot.writeFieldBegin(MESSAGE_FIELD); + oprot.writeString(getMessage()); + oprot.writeFieldEnd(); + } + oprot.writeFieldBegin(TYPE_FIELD); + oprot.writeI32(type_); + oprot.writeFieldEnd(); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java new file mode 100644 index 000000000..66f768896 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TAsyncProcessor.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.server.AbstractNonblockingServer.AsyncFrameBuffer; + +public interface TAsyncProcessor { + /** + * Process a single frame. + + * <b>Note:</b> Implementations must call fb.responseReady() once processing + * is complete + * + * @throws TException if the frame cannot be processed + */ + public void process(final AsyncFrameBuffer fb) throws TException; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java new file mode 100644 index 000000000..e1489d530 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBase.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.Serializable; + +import org.apache.thrift.protocol.TProtocol; + +/** + * Generic base interface for generated Thrift objects. + * + */ +public interface TBase<T extends TBase<T,F>, F extends TFieldIdEnum> extends Comparable<T>, TSerializable, Serializable { + + /** + * Get the F instance that corresponds to fieldId. + */ + public F fieldForId(int fieldId); + + /** + * Check if a field is currently set or unset. + * + * @param field + */ + public boolean isSet(F field); + + /** + * Get a field's value by field variable. Primitive types will be wrapped in + * the appropriate "boxed" types. + * + * @param field + */ + public Object getFieldValue(F field); + + /** + * Set a field's value by field variable. Primitive types must be "boxed" in + * the appropriate object wrapper type. + * + * @param field + */ + public void setFieldValue(F field, Object value); + + public T deepCopy(); + + /** + * Return to the state of having just been initialized, as though you had just + * called the default constructor. + */ + public void clear(); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java new file mode 100644 index 000000000..f13f068ef --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseAsyncProcessor.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.*; +import org.apache.thrift.async.AsyncMethodCallback; + +import org.apache.thrift.server.AbstractNonblockingServer.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Map; + +public class TBaseAsyncProcessor<I> implements TAsyncProcessor, TProcessor { + protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName()); + + final I iface; + final Map<String,AsyncProcessFunction<I, ? extends TBase,?>> processMap; + + public TBaseAsyncProcessor(I iface, Map<String, AsyncProcessFunction<I, ? extends TBase,?>> processMap) { + this.iface = iface; + this.processMap = processMap; + } + + public Map<String,AsyncProcessFunction<I, ? extends TBase,?>> getProcessMapView() { + return Collections.unmodifiableMap(processMap); + } + + public void process(final AsyncFrameBuffer fb) throws TException { + + final TProtocol in = fb.getInputProtocol(); + final TProtocol out = fb.getOutputProtocol(); + + //Find processing function + final TMessage msg = in.readMessageBegin(); + AsyncProcessFunction fn = processMap.get(msg.name); + if (fn == null) { + TProtocolUtil.skip(in, TType.STRUCT); + in.readMessageEnd(); + + TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, + "Invalid method name: '" + msg.name + "'"); + LOGGER.debug("Invalid method name", x); + + // this means it is a two-way request, so we can send a reply + if (msg.type == TMessageType.CALL) { + out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid)); + x.write(out); + out.writeMessageEnd(); + out.getTransport().flush(); + } + fb.responseReady(); + return; + } + + //Get Args + TBase args = fn.getEmptyArgsInstance(); + + try { + args.read(in); + } catch (TProtocolException e) { + in.readMessageEnd(); + + TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR, + e.getMessage()); + LOGGER.debug("Could not retrieve function arguments", x); + + if (!fn.isOneway()) { + out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid)); + x.write(out); + out.writeMessageEnd(); + out.getTransport().flush(); + } + fb.responseReady(); + return; + } + in.readMessageEnd(); + + if (fn.isOneway()) { + fb.responseReady(); + } + + //start off processing function + AsyncMethodCallback resultHandler = fn.getResultHandler(fb, msg.seqid); + try { + fn.start(iface, args, resultHandler); + } catch (Exception e) { + LOGGER.debug("Exception handling function", e); + resultHandler.onError(e); + } + return; + } + + @Override + public void process(TProtocol in, TProtocol out) throws TException { + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java new file mode 100644 index 000000000..6f6c6ebf5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseHelper.java @@ -0,0 +1,297 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public final class TBaseHelper { + + private TBaseHelper(){} + + private static final Comparator comparator = new NestedStructureComparator(); + + public static int compareTo(Object o1, Object o2) { + if (o1 instanceof Comparable) { + return compareTo((Comparable)o1, (Comparable)o2); + } else if (o1 instanceof List) { + return compareTo((List)o1, (List)o2); + } else if (o1 instanceof Set) { + return compareTo((Set)o1, (Set)o2); + } else if (o1 instanceof Map) { + return compareTo((Map)o1, (Map)o2); + } else if (o1 instanceof byte[]) { + return compareTo((byte[])o1, (byte[])o2); + } else { + throw new IllegalArgumentException("Cannot compare objects of type " + o1.getClass()); + } + } + + public static int compareTo(boolean a, boolean b) { + return Boolean.compare(a, b); + } + + public static int compareTo(byte a, byte b) { + return Byte.compare(a, b); + } + + public static int compareTo(short a, short b) { + return Short.compare(a,b); + } + + public static int compareTo(int a, int b) { + return Integer.compare(a, b); + } + + public static int compareTo(long a, long b) { + return Long.compare(a, b); + } + + public static int compareTo(double a, double b) { + return Double.compare(a, b); + } + + public static int compareTo(String a, String b) { + return a.compareTo(b); + } + + public static int compareTo(byte[] a, byte[] b) { + int compare = compareTo(a.length, b.length); + if (compare == 0) { + for (int i = 0; i < a.length; i++) { + compare = compareTo(a[i], b[i]); + if (compare != 0) { + break; + } + } + } + return compare; + } + + public static int compareTo(Comparable a, Comparable b) { + return a.compareTo(b); + } + + public static int compareTo(List a, List b) { + int compare = compareTo(a.size(), b.size()); + if (compare == 0) { + for (int i = 0; i < a.size(); i++) { + compare = comparator.compare(a.get(i), b.get(i)); + if (compare != 0) { + break; + } + } + } + return compare; + } + + public static int compareTo(Set a, Set b) { + int compare = compareTo(a.size(), b.size()); + if (compare == 0) { + ArrayList sortedA = new ArrayList(a); + ArrayList sortedB = new ArrayList(b); + + Collections.sort(sortedA, comparator); + Collections.sort(sortedB, comparator); + + Iterator iterA = sortedA.iterator(); + Iterator iterB = sortedB.iterator(); + + // Compare each item. + while (iterA.hasNext() && iterB.hasNext()) { + compare = comparator.compare(iterA.next(), iterB.next()); + if (compare != 0) { + break; + } + } + } + return compare; + } + + public static int compareTo(Map a, Map b) { + int lastComparison = compareTo(a.size(), b.size()); + if (lastComparison != 0) { + return lastComparison; + } + + // Sort a and b so we can compare them. + SortedMap sortedA = new TreeMap(comparator); + sortedA.putAll(a); + Iterator<Map.Entry> iterA = sortedA.entrySet().iterator(); + SortedMap sortedB = new TreeMap(comparator); + sortedB.putAll(b); + Iterator<Map.Entry> iterB = sortedB.entrySet().iterator(); + + // Compare each item. + while (iterA.hasNext() && iterB.hasNext()) { + Map.Entry entryA = iterA.next(); + Map.Entry entryB = iterB.next(); + lastComparison = comparator.compare(entryA.getKey(), entryB.getKey()); + if (lastComparison != 0) { + return lastComparison; + } + lastComparison = comparator.compare(entryA.getValue(), entryB.getValue()); + if (lastComparison != 0) { + return lastComparison; + } + } + + return 0; + } + + /** + * Comparator to compare items inside a structure (e.g. a list, set, or map). + */ + private static class NestedStructureComparator implements Comparator, Serializable { + public int compare(Object oA, Object oB) { + if (oA == null && oB == null) { + return 0; + } else if (oA == null) { + return -1; + } else if (oB == null) { + return 1; + } else if (oA instanceof List) { + return compareTo((List)oA, (List)oB); + } else if (oA instanceof Set) { + return compareTo((Set)oA, (Set)oB); + } else if (oA instanceof Map) { + return compareTo((Map)oA, (Map)oB); + } else if (oA instanceof byte[]) { + return compareTo((byte[])oA, (byte[])oB); + } else { + return compareTo((Comparable)oA, (Comparable)oB); + } + } + } + + public static void toString(Collection<ByteBuffer> bbs, StringBuilder sb) { + Iterator<ByteBuffer> it = bbs.iterator(); + if (!it.hasNext()) { + sb.append("[]"); + } else { + sb.append("["); + while (true) { + ByteBuffer bb = it.next(); + org.apache.thrift.TBaseHelper.toString(bb, sb); + if (!it.hasNext()) { + sb.append("]"); + return; + } else { + sb.append(", "); + } + } + } + } + + public static void toString(ByteBuffer bb, StringBuilder sb) { + byte[] buf = bb.array(); + + int arrayOffset = bb.arrayOffset(); + int offset = arrayOffset + bb.position(); + int origLimit = arrayOffset + bb.limit(); + int limit = (origLimit - offset > 128) ? offset + 128 : origLimit; + + for (int i = offset; i < limit; i++) { + if (i > offset) { + sb.append(" "); + } + sb.append(paddedByteString(buf[i])); + } + if (origLimit != limit) { + sb.append("..."); + } + } + + public static String paddedByteString(byte b) { + int extended = (b | 0x100) & 0x1ff; + return Integer.toHexString(extended).toUpperCase().substring(1); + } + + public static byte[] byteBufferToByteArray(ByteBuffer byteBuffer) { + if (wrapsFullArray(byteBuffer)) { + return byteBuffer.array(); + } + byte[] target = new byte[byteBuffer.remaining()]; + byteBufferToByteArray(byteBuffer, target, 0); + return target; + } + + public static boolean wrapsFullArray(ByteBuffer byteBuffer) { + return byteBuffer.hasArray() + && byteBuffer.position() == 0 + && byteBuffer.arrayOffset() == 0 + && byteBuffer.remaining() == byteBuffer.capacity(); + } + + public static int byteBufferToByteArray(ByteBuffer byteBuffer, byte[] target, int offset) { + int remaining = byteBuffer.remaining(); + System.arraycopy(byteBuffer.array(), + byteBuffer.arrayOffset() + byteBuffer.position(), + target, + offset, + remaining); + return remaining; + } + + public static ByteBuffer rightSize(ByteBuffer in) { + if (in == null) { + return null; + } + if (wrapsFullArray(in)) { + return in; + } + return ByteBuffer.wrap(byteBufferToByteArray(in)); + } + + public static ByteBuffer copyBinary(final ByteBuffer orig) { + if (orig == null) { + return null; + } + ByteBuffer copy = ByteBuffer.wrap(new byte[orig.remaining()]); + if (orig.hasArray()) { + System.arraycopy(orig.array(), orig.arrayOffset() + orig.position(), copy.array(), 0, orig.remaining()); + } else { + orig.slice().get(copy.array()); + } + + return copy; + } + + public static byte[] copyBinary(final byte[] orig) { + return (orig == null) ? null : Arrays.copyOf(orig, orig.length); + } + + public static int hashCode(long value) { + return Long.hashCode(value); + } + + public static int hashCode(double value) { + return Double.hashCode(value); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java new file mode 100644 index 000000000..55a0f15d3 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TBaseProcessor.java @@ -0,0 +1,41 @@ +package org.apache.thrift; + +import java.util.Collections; +import java.util.Map; + +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolUtil; +import org.apache.thrift.protocol.TType; + +public abstract class TBaseProcessor<I> implements TProcessor { + private final I iface; + private final Map<String,ProcessFunction<I, ? extends TBase>> processMap; + + protected TBaseProcessor(I iface, Map<String, ProcessFunction<I, ? extends TBase>> processFunctionMap) { + this.iface = iface; + this.processMap = processFunctionMap; + } + + public Map<String,ProcessFunction<I, ? extends TBase>> getProcessMapView() { + return Collections.unmodifiableMap(processMap); + } + + @Override + public void process(TProtocol in, TProtocol out) throws TException { + TMessage msg = in.readMessageBegin(); + ProcessFunction fn = processMap.get(msg.name); + if (fn == null) { + TProtocolUtil.skip(in, TType.STRUCT); + in.readMessageEnd(); + TApplicationException x = new TApplicationException(TApplicationException.UNKNOWN_METHOD, "Invalid method name: '"+msg.name+"'"); + out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid)); + x.write(out); + out.writeMessageEnd(); + out.getTransport().flush(); + } else { + fn.process(msg.seqid, in, out, iface); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java new file mode 100644 index 000000000..3a2d56c88 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TByteArrayOutputStream.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.Charset; + +/** + * Class that allows access to the underlying buf without doing deep + * copies on it. + * + */ +public class TByteArrayOutputStream extends ByteArrayOutputStream { + + private final int initialSize; + + public TByteArrayOutputStream(int size) { + super(size); + this.initialSize = size; + } + + public TByteArrayOutputStream() { + this(32); + } + + public byte[] get() { + return buf; + } + + public void reset() { + super.reset(); + if (buf.length > initialSize) { + buf = new byte[initialSize]; + } + } + + public int len() { + return count; + } + + public String toString(Charset charset) { + return new String(buf, 0, count, charset); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java new file mode 100644 index 000000000..d1d396609 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TDeserializer.java @@ -0,0 +1,339 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.protocol.TProtocolUtil; +import org.apache.thrift.protocol.TType; +import org.apache.thrift.transport.TMemoryInputTransport; + +/** + * Generic utility for easily deserializing objects from a byte array or Java + * String. + * + */ +public class TDeserializer { + private final TProtocol protocol_; + private final TMemoryInputTransport trans_; + + /** + * Create a new TDeserializer that uses the TBinaryProtocol by default. + */ + public TDeserializer() { + this(new TBinaryProtocol.Factory()); + } + + /** + * Create a new TDeserializer. It will use the TProtocol specified by the + * factory that is passed in. + * + * @param protocolFactory Factory to create a protocol + */ + public TDeserializer(TProtocolFactory protocolFactory) { + trans_ = new TMemoryInputTransport(); + protocol_ = protocolFactory.getProtocol(trans_); + } + + /** + * Deserialize the Thrift object from a byte array. + * + * @param base The object to read into + * @param bytes The array to read from + */ + public void deserialize(TBase base, byte[] bytes) throws TException { + deserialize(base, bytes, 0, bytes.length); + } + + /** + * Deserialize the Thrift object from a byte array. + * + * @param base The object to read into + * @param bytes The array to read from + * @param offset The offset into {@code bytes} + * @param length The length to read from {@code bytes} + */ + public void deserialize(TBase base, byte[] bytes, int offset, int length) throws TException { + try { + trans_.reset(bytes, offset, length); + base.read(protocol_); + } finally { + trans_.clear(); + protocol_.reset(); + } + } + + /** + * Deserialize the Thrift object from a Java string, using a specified + * character set for decoding. + * + * @param base The object to read into + * @param data The string to read from + * @param charset Valid JVM charset + */ + public void deserialize(TBase base, String data, String charset) throws TException { + try { + deserialize(base, data.getBytes(charset)); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset); + } finally { + protocol_.reset(); + } + } + + /** + * Deserialize only a single Thrift object (addressed by recursively using field id) + * from a byte record. + * @param tb The object to read into + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path tb + * @param fieldIdPathRest The rest FieldId's that define a path tb + * @throws TException + */ + public void partialDeserialize(TBase tb, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + try { + if (locateField(bytes, fieldIdPathFirst, fieldIdPathRest) != null) { + // if this line is reached, iprot will be positioned at the start of tb. + tb.read(protocol_); + } + } catch (Exception e) { + throw new TException(e); + } finally { + trans_.clear(); + protocol_.reset(); + } + } + + /** + * Deserialize only a boolean field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to a boolean field + * @param fieldIdPathRest The rest FieldId's that define a path to a boolean field + * @throws TException + */ + public Boolean partialDeserializeBool(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (Boolean) partialDeserializeField(TType.BOOL, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only a byte field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to a byte field + * @param fieldIdPathRest The rest FieldId's that define a path to a byte field + * @throws TException + */ + public Byte partialDeserializeByte(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (Byte) partialDeserializeField(TType.BYTE, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only a double field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to a double field + * @param fieldIdPathRest The rest FieldId's that define a path to a double field + * @throws TException + */ + public Double partialDeserializeDouble(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (Double) partialDeserializeField(TType.DOUBLE, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only an i16 field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to an i16 field + * @param fieldIdPathRest The rest FieldId's that define a path to an i16 field + * @throws TException + */ + public Short partialDeserializeI16(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (Short) partialDeserializeField(TType.I16, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only an i32 field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to an i32 field + * @param fieldIdPathRest The rest FieldId's that define a path to an i32 field + * @throws TException + */ + public Integer partialDeserializeI32(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (Integer) partialDeserializeField(TType.I32, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only an i64 field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to an i64 field + * @param fieldIdPathRest The rest FieldId's that define a path to an i64 field + * @throws TException + */ + public Long partialDeserializeI64(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (Long) partialDeserializeField(TType.I64, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only a string field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to a string field + * @param fieldIdPathRest The rest FieldId's that define a path to a string field + * @throws TException + */ + public String partialDeserializeString(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + return (String) partialDeserializeField(TType.STRING, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only a binary field (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to a binary field + * @param fieldIdPathRest The rest FieldId's that define a path to a binary field + * @throws TException + */ + public ByteBuffer partialDeserializeByteArray(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + // TType does not have binary, so we use the arbitrary num 100 + return (ByteBuffer) partialDeserializeField((byte)100, bytes, fieldIdPathFirst, fieldIdPathRest); + } + + /** + * Deserialize only the id of the field set in a TUnion (addressed by recursively using field id) + * from a byte record. + * @param bytes The serialized object to read from + * @param fieldIdPathFirst First of the FieldId's that define a path to a TUnion + * @param fieldIdPathRest The rest FieldId's that define a path to a TUnion + * @throws TException + */ + public Short partialDeserializeSetFieldIdInUnion(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + try { + TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest); + if (field != null){ + protocol_.readStructBegin(); // The Union + return protocol_.readFieldBegin().id; // The field set in the union + } + return null; + } catch (Exception e) { + throw new TException(e); + } finally { + trans_.clear(); + protocol_.reset(); + } + } + + private Object partialDeserializeField(byte ttype, byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + try { + TField field = locateField(bytes, fieldIdPathFirst, fieldIdPathRest); + if (field != null) { + if (ttype == field.type) { + // if this point is reached, iprot will be positioned at the start of + // the field + switch (ttype) { + case TType.BOOL: + return protocol_.readBool(); + case TType.BYTE: + return protocol_.readByte(); + case TType.DOUBLE: + return protocol_.readDouble(); + case TType.I16: + return protocol_.readI16(); + case TType.I32: + return protocol_.readI32(); + case TType.I64: + return protocol_.readI64(); + case TType.STRING: + return protocol_.readString(); + default: + return null; + } + } + // hack to differentiate between string and binary + if (ttype == 100 && field.type == TType.STRING) { + return protocol_.readBinary(); + } + } + return null; + } catch (Exception e) { + throw new TException(e); + } finally { + trans_.clear(); + protocol_.reset(); + } + } + + private TField locateField(byte[] bytes, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + trans_.reset(bytes); + + TFieldIdEnum[] fieldIdPath = new TFieldIdEnum[fieldIdPathRest.length + 1]; + fieldIdPath[0] = fieldIdPathFirst; + System.arraycopy(fieldIdPathRest, 0, fieldIdPath, 1, fieldIdPathRest.length); + + // index into field ID path being currently searched for + int curPathIndex = 0; + + // this will be the located field, or null if it is not located + TField field = null; + + protocol_.readStructBegin(); + + while (curPathIndex < fieldIdPath.length) { + field = protocol_.readFieldBegin(); + // we can stop searching if we either see a stop or we go past the field + // id we're looking for (since fields should now be serialized in asc + // order). + if (field.type == TType.STOP || field.id > fieldIdPath[curPathIndex].getThriftFieldId()) { + return null; + } + + if (field.id != fieldIdPath[curPathIndex].getThriftFieldId()) { + // Not the field we're looking for. Skip field. + TProtocolUtil.skip(protocol_, field.type); + protocol_.readFieldEnd(); + } else { + // This field is the next step in the path. Step into field. + curPathIndex++; + if (curPathIndex < fieldIdPath.length) { + protocol_.readStructBegin(); + } + } + } + return field; + } + + /** + * Deserialize the Thrift object from a Java string, using the default JVM + * charset encoding. + * + * @param base The object to read into + * @param data The string to read from + */ + public void fromString(TBase base, String data) throws TException { + deserialize(base, data.getBytes()); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java new file mode 100644 index 000000000..325fdece7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnum.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +public interface TEnum { + public int getValue(); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java new file mode 100644 index 000000000..fbc778751 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TEnumHelper.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.lang.NoSuchMethodException; +import java.lang.IllegalAccessException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Utility class with static methods for interacting with TEnum + */ +public class TEnumHelper { + + /** + * Given a TEnum class and integer value, this method will return + * the associated constant from the given TEnum class. + * This method MUST be modified should the name of the 'findByValue' method + * change. + * + * @param enumClass TEnum from which to return a matching constant. + * @param value Value for which to return the constant. + * + * @return The constant in 'enumClass' whose value is 'value' or null if + * something went wrong. + */ + public static TEnum getByValue(Class<? extends TEnum> enumClass, int value) { + try { + Method method = enumClass.getMethod("findByValue", int.class); + return (TEnum) method.invoke(null, value); + } catch (NoSuchMethodException nsme) { + return null; + } catch (IllegalAccessException iae) { + return null; + } catch (InvocationTargetException ite) { + return null; + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java new file mode 100644 index 000000000..f84f4812e --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TException.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Generic exception class for Thrift. + * + */ +public class TException extends Exception { + + private static final long serialVersionUID = 1L; + + public TException() { + super(); + } + + public TException(String message) { + super(message); + } + + public TException(Throwable cause) { + super(cause); + } + + public TException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java new file mode 100644 index 000000000..2956fba0b --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldIdEnum.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Interface for all generated struct Fields objects. + */ +public interface TFieldIdEnum { + /** + * Get the Thrift field id for the named field. + */ + public short getThriftFieldId(); + + /** + * Get the field's name, exactly as in the IDL. + */ + public String getFieldName(); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java new file mode 100644 index 000000000..74bac4eff --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TFieldRequirementType.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Requirement type constants. + * + */ +public final class TFieldRequirementType { + public static final byte REQUIRED = 1; + public static final byte OPTIONAL = 2; + public static final byte DEFAULT = 3; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java new file mode 100644 index 000000000..c49486217 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TMultiplexedProcessor.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.*; + +import java.util.Map; +import java.util.HashMap; + +/** + * <code>TMultiplexedProcessor</code> is a <code>TProcessor</code> allowing + * a single <code>TServer</code> to provide multiple services. + * + * <p>To do so, you instantiate the processor and then register additional + * processors with it, as shown in the following example:</p> + * + * <blockquote><code> + * TMultiplexedProcessor processor = new TMultiplexedProcessor(); + * + * processor.registerProcessor( + * "Calculator", + * new Calculator.Processor(new CalculatorHandler())); + * + * processor.registerProcessor( + * "WeatherReport", + * new WeatherReport.Processor(new WeatherReportHandler())); + * + * TServerTransport t = new TServerSocket(9090); + * TSimpleServer server = new TSimpleServer(processor, t); + * + * server.serve(); + * </code></blockquote> + */ +public class TMultiplexedProcessor implements TProcessor { + + private final Map<String,TProcessor> SERVICE_PROCESSOR_MAP + = new HashMap<String,TProcessor>(); + private TProcessor defaultProcessor; + + /** + * 'Register' a service with this <code>TMultiplexedProcessor</code>. This + * allows us to broker requests to individual services by using the service + * name to select them at request time. + * + * @param serviceName Name of a service, has to be identical to the name + * declared in the Thrift IDL, e.g. "WeatherReport". + * @param processor Implementation of a service, usually referred to + * as "handlers", e.g. WeatherReportHandler implementing WeatherReport.Iface. + */ + public void registerProcessor(String serviceName, TProcessor processor) { + SERVICE_PROCESSOR_MAP.put(serviceName, processor); + } + + /** + * Register a service to be called to process queries without service name + * @param processor + */ + public void registerDefault(TProcessor processor) { + defaultProcessor = processor; + } + + /** + * This implementation of <code>process</code> performs the following steps: + * + * <ol> + * <li>Read the beginning of the message.</li> + * <li>Extract the service name from the message.</li> + * <li>Using the service name to locate the appropriate processor.</li> + * <li>Dispatch to the processor, with a decorated instance of TProtocol + * that allows readMessageBegin() to return the original TMessage.</li> + * </ol> + * + * @throws TProtocolException If the message type is not CALL or ONEWAY, if + * the service name was not found in the message, or if the service + * name was not found in the service map. You called {@link #registerProcessor(String, TProcessor) registerProcessor} + * during initialization, right? :) + */ + public void process(TProtocol iprot, TProtocol oprot) throws TException { + /* + Use the actual underlying protocol (e.g. TBinaryProtocol) to read the + message header. This pulls the message "off the wire", which we'll + deal with at the end of this method. + */ + TMessage message = iprot.readMessageBegin(); + + if (message.type != TMessageType.CALL && message.type != TMessageType.ONEWAY) { + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "This should not have happened!?"); + } + + // Extract the service name + int index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR); + if (index < 0) { + if (defaultProcessor != null) { + // Dispatch processing to the stored processor + defaultProcessor.process(new StoredMessageProtocol(iprot, message), oprot); + return; + } + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Service name not found in message name: " + message.name + ". Did you " + + "forget to use a TMultiplexProtocol in your client?"); + } + + // Create a new TMessage, something that can be consumed by any TProtocol + String serviceName = message.name.substring(0, index); + TProcessor actualProcessor = SERVICE_PROCESSOR_MAP.get(serviceName); + if (actualProcessor == null) { + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Service name not found: " + serviceName + ". Did you forget " + + "to call registerProcessor()?"); + } + + // Create a new TMessage, removing the service name + TMessage standardMessage = new TMessage( + message.name.substring(serviceName.length()+TMultiplexedProtocol.SEPARATOR.length()), + message.type, + message.seqid + ); + + // Dispatch processing to the stored processor + actualProcessor.process(new StoredMessageProtocol(iprot, standardMessage), oprot); + } + + /** + * Our goal was to work with any protocol. In order to do that, we needed + * to allow them to call readMessageBegin() and get a TMessage in exactly + * the standard format, without the service name prepended to TMessage.name. + */ + private static class StoredMessageProtocol extends TProtocolDecorator { + TMessage messageBegin; + public StoredMessageProtocol(TProtocol protocol, TMessage messageBegin) { + super(protocol); + this.messageBegin = messageBegin; + } + @Override + public TMessage readMessageBegin() throws TException { + return messageBegin; + } + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java new file mode 100644 index 000000000..382d978db --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchClient.java @@ -0,0 +1,399 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +/** + * This class uses a single thread to set up non-blocking sockets to a set + * of remote servers (hostname and port pairs), and sends a same request to + * all these servers. It then fetches responses from servers. + * + * Parameters: + * int maxRecvBufBytesPerServer - an upper limit for receive buffer size + * per server (in byte). If a response from a server exceeds this limit, the + * client will not allocate memory or read response data for it. + * + * int fetchTimeoutSeconds - time limit for fetching responses from all + * servers (in second). After the timeout, the fetch job is stopped and + * available responses are returned. + * + * ByteBuffer requestBuf - request message that is sent to all servers. + * + * Output: + * Responses are stored in an array of ByteBuffers. Index of elements in + * this array corresponds to index of servers in the server list. Content in + * a ByteBuffer may be in one of the following forms: + * 1. First 4 bytes form an integer indicating length of following data, + * then followed by the data. + * 2. First 4 bytes form an integer indicating length of following data, + * then followed by nothing - this happens when the response data size + * exceeds maxRecvBufBytesPerServer, and the client will not read any + * response data. + * 3. No data in the ByteBuffer - this happens when the server does not + * return any response within fetchTimeoutSeconds. + * + * In some special cases (no servers are given, fetchTimeoutSeconds less + * than or equal to 0, requestBuf is null), the return is null. + * + * Note: + * It assumes all remote servers are TNonblockingServers and use + * TFramedTransport. + * + */ +public class TNonblockingMultiFetchClient { + + private static final Logger LOGGER = LoggerFactory.getLogger( + TNonblockingMultiFetchClient.class.getName() + ); + + // if the size of the response msg exceeds this limit (in byte), we will + // not read the msg + private int maxRecvBufBytesPerServer; + + // time limit for fetching data from all servers (in second) + private int fetchTimeoutSeconds; + + // store request that will be sent to servers + private ByteBuffer requestBuf; + private ByteBuffer requestBufDuplication; + + // a list of remote servers + private List<InetSocketAddress> servers; + + // store fetch results + private TNonblockingMultiFetchStats stats; + private ByteBuffer[] recvBuf; + + public TNonblockingMultiFetchClient(int maxRecvBufBytesPerServer, + int fetchTimeoutSeconds, ByteBuffer requestBuf, + List<InetSocketAddress> servers) { + this.maxRecvBufBytesPerServer = maxRecvBufBytesPerServer; + this.fetchTimeoutSeconds = fetchTimeoutSeconds; + this.requestBuf = requestBuf; + this.servers = servers; + + stats = new TNonblockingMultiFetchStats(); + recvBuf = null; + } + + public synchronized int getMaxRecvBufBytesPerServer() { + return maxRecvBufBytesPerServer; + } + + public synchronized int getFetchTimeoutSeconds() { + return fetchTimeoutSeconds; + } + + /** + * return a duplication of requestBuf, so that requestBuf will not + * be modified by others. + */ + public synchronized ByteBuffer getRequestBuf() { + if (requestBuf == null) { + return null; + } else { + if (requestBufDuplication == null) { + requestBufDuplication = requestBuf.duplicate(); + } + return requestBufDuplication; + } + } + + public synchronized List<InetSocketAddress> getServerList() { + if (servers == null) { + return null; + } + return Collections.unmodifiableList(servers); + } + + public synchronized TNonblockingMultiFetchStats getFetchStats() { + return stats; + } + + /** + * main entry function for fetching from servers + */ + public synchronized ByteBuffer[] fetch() { + // clear previous results + recvBuf = null; + stats.clear(); + + if (servers == null || servers.size() == 0 || + requestBuf == null || fetchTimeoutSeconds <= 0) { + return recvBuf; + } + + ExecutorService executor = Executors.newSingleThreadExecutor(); + MultiFetch multiFetch = new MultiFetch(); + FutureTask<?> task = new FutureTask(multiFetch, null); + executor.execute(task); + try { + task.get(fetchTimeoutSeconds, TimeUnit.SECONDS); + } catch(InterruptedException ie) { + // attempt to cancel execution of the task. + task.cancel(true); + LOGGER.error("interrupted during fetch: "+ie.toString()); + } catch(ExecutionException ee) { + // attempt to cancel execution of the task. + task.cancel(true); + LOGGER.error("exception during fetch: "+ee.toString()); + } catch(TimeoutException te) { + // attempt to cancel execution of the task. + task.cancel(true); + LOGGER.error("timeout for fetch: "+te.toString()); + } + + executor.shutdownNow(); + multiFetch.close(); + return recvBuf; + } + + /** + * Private class that does real fetch job. + * Users are not allowed to directly use this class, as its run() + * function may run forever. + */ + private class MultiFetch implements Runnable { + private Selector selector; + + /** + * main entry function for fetching. + * + * Server responses are stored in TNonblocingMultiFetchClient.recvBuf, + * and fetch statistics is in TNonblockingMultiFetchClient.stats. + * + * Sanity check for parameters has been done in + * TNonblockingMultiFetchClient before calling this function. + */ + public void run() { + long t1 = System.currentTimeMillis(); + + int numTotalServers = servers.size(); + stats.setNumTotalServers(numTotalServers); + + // buffer for receiving response from servers + recvBuf = new ByteBuffer[numTotalServers]; + // buffer for sending request + ByteBuffer sendBuf[] = new ByteBuffer[numTotalServers]; + long numBytesRead[] = new long[numTotalServers]; + int frameSize[] = new int[numTotalServers]; + boolean hasReadFrameSize[] = new boolean[numTotalServers]; + + try { + selector = Selector.open(); + } catch (IOException e) { + LOGGER.error("selector opens error: "+e.toString()); + return; + } + + for (int i = 0; i < numTotalServers; i++) { + // create buffer to send request to server. + sendBuf[i] = requestBuf.duplicate(); + // create buffer to read response's frame size from server + recvBuf[i] = ByteBuffer.allocate(4); + stats.incTotalRecvBufBytes(4); + + InetSocketAddress server = servers.get(i); + SocketChannel s = null; + SelectionKey key = null; + try { + s = SocketChannel.open(); + s.configureBlocking(false); + // now this method is non-blocking + s.connect(server); + key = s.register(selector, s.validOps()); + // attach index of the key + key.attach(i); + } catch (Exception e) { + stats.incNumConnectErrorServers(); + String err = String.format("set up socket to server %s error: %s", + server.toString(), e.toString()); + LOGGER.error(err); + // free resource + if (s != null) { + try {s.close();} catch (Exception ex) {} + } + if (key != null) { + key.cancel(); + } + } + } + + // wait for events + while (stats.getNumReadCompletedServers() + + stats.getNumConnectErrorServers() < stats.getNumTotalServers()) { + // if the thread is interrupted (e.g., task is cancelled) + if (Thread.currentThread().isInterrupted()) { + return; + } + + try{ + selector.select(); + } catch (Exception e) { + LOGGER.error("selector selects error: "+e.toString()); + continue; + } + + Iterator<SelectionKey> it = selector.selectedKeys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = it.next(); + it.remove(); + + // get previously attached index + int index = (Integer)selKey.attachment(); + + if (selKey.isValid() && selKey.isConnectable()) { + // if this socket throws an exception (e.g., connection refused), + // print error msg and skip it. + try { + SocketChannel sChannel = (SocketChannel)selKey.channel(); + sChannel.finishConnect(); + } catch (Exception e) { + stats.incNumConnectErrorServers(); + String err = String.format("socket %d connects to server %s " + + "error: %s", + index, servers.get(index).toString(), e.toString()); + LOGGER.error(err); + } + } + + if (selKey.isValid() && selKey.isWritable()) { + if (sendBuf[index].hasRemaining()) { + // if this socket throws an exception, print error msg and + // skip it. + try { + SocketChannel sChannel = (SocketChannel)selKey.channel(); + sChannel.write(sendBuf[index]); + } catch (Exception e) { + String err = String.format("socket %d writes to server %s " + + "error: %s", + index, servers.get(index).toString(), e.toString()); + LOGGER.error(err); + } + } + } + + if (selKey.isValid() && selKey.isReadable()) { + // if this socket throws an exception, print error msg and + // skip it. + try { + SocketChannel sChannel = (SocketChannel)selKey.channel(); + int bytesRead = sChannel.read(recvBuf[index]); + + if (bytesRead > 0) { + numBytesRead[index] += bytesRead; + + if (!hasReadFrameSize[index] && + recvBuf[index].remaining()==0) { + // if the frame size has been read completely, then prepare + // to read the actual frame. + frameSize[index] = recvBuf[index].getInt(0); + + if (frameSize[index] <= 0) { + stats.incNumInvalidFrameSize(); + String err = String.format("Read an invalid frame size %d" + + " from %s. Does the server use TFramedTransport? ", + frameSize[index], servers.get(index).toString()); + LOGGER.error(err); + sChannel.close(); + continue; + } + + if (frameSize[index] + 4 > stats.getMaxResponseBytes()) { + stats.setMaxResponseBytes(frameSize[index]+4); + } + + if (frameSize[index] + 4 > maxRecvBufBytesPerServer) { + stats.incNumOverflowedRecvBuf(); + String err = String.format("Read frame size %d from %s," + + " total buffer size would exceed limit %d", + frameSize[index], servers.get(index).toString(), + maxRecvBufBytesPerServer); + LOGGER.error(err); + sChannel.close(); + continue; + } + + // reallocate buffer for actual frame data + recvBuf[index] = ByteBuffer.allocate(frameSize[index] + 4); + recvBuf[index].putInt(frameSize[index]); + + stats.incTotalRecvBufBytes(frameSize[index]); + hasReadFrameSize[index] = true; + } + + if (hasReadFrameSize[index] && + numBytesRead[index] >= frameSize[index]+4) { + // has read all data + sChannel.close(); + stats.incNumReadCompletedServers(); + long t2 = System.currentTimeMillis(); + stats.setReadTime(t2-t1); + } + } + } catch (Exception e) { + String err = String.format("socket %d reads from server %s " + + "error: %s", + index, servers.get(index).toString(), e.toString()); + LOGGER.error(err); + } + } + } + } + } + + /** + * dispose any resource allocated + */ + public void close() { + try { + if (selector.isOpen()) { + Iterator<SelectionKey> it = selector.keys().iterator(); + while (it.hasNext()) { + SelectionKey selKey = it.next(); + SocketChannel sChannel = (SocketChannel)selKey.channel(); + sChannel.close(); + } + + selector.close(); + } + } catch (IOException e) { + LOGGER.error("free resource error: "+e.toString()); + } + } + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java new file mode 100644 index 000000000..90b86208b --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TNonblockingMultiFetchStats.java @@ -0,0 +1,80 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * This class keeps track of statistics for TNonblockinMultiFetchClient. + */ +public class TNonblockingMultiFetchStats { + private int numTotalServers; + private int numReadCompletedServers; + private int numConnectErrorServers; + private int totalRecvBufBytes; + private int maxResponseBytes; + private int numOverflowedRecvBuf; + private int numInvalidFrameSize; + // time from the beginning of fetch() function to the reading finish + // time of the last socket (in milli-second) + private long readTime; + + public TNonblockingMultiFetchStats() { + clear(); + } + + public void clear() { + numTotalServers = 0; + numReadCompletedServers = 0; + numConnectErrorServers = 0; + totalRecvBufBytes = 0; + maxResponseBytes = 0; + numOverflowedRecvBuf = 0; + numInvalidFrameSize = 0; + readTime = 0; + } + + public String toString() { + String stats = String.format("numTotalServers=%d, " + + "numReadCompletedServers=%d, numConnectErrorServers=%d, " + + "numUnresponsiveServers=%d, totalRecvBufBytes=%fM, " + + "maxResponseBytes=%d, numOverflowedRecvBuf=%d, " + + "numInvalidFrameSize=%d, readTime=%dms", + numTotalServers, numReadCompletedServers, numConnectErrorServers, + (numTotalServers-numReadCompletedServers-numConnectErrorServers), + totalRecvBufBytes/1024.0/1024, maxResponseBytes, numOverflowedRecvBuf, + numInvalidFrameSize, readTime); + return stats; + } + + public void setNumTotalServers(int val) { numTotalServers = val; } + public void setMaxResponseBytes(int val) { maxResponseBytes = val; } + public void setReadTime(long val) { readTime = val; } + public void incNumReadCompletedServers() { numReadCompletedServers++; } + public void incNumConnectErrorServers() { numConnectErrorServers++; } + public void incNumOverflowedRecvBuf() { numOverflowedRecvBuf++; } + public void incTotalRecvBufBytes(int val) { totalRecvBufBytes += val; } + public void incNumInvalidFrameSize() { numInvalidFrameSize++; } + + public int getMaxResponseBytes() { return maxResponseBytes; } + public int getNumReadCompletedServers() { return numReadCompletedServers; } + public int getNumConnectErrorServers() { return numConnectErrorServers; } + public int getNumTotalServers() { return numTotalServers; } + public int getNumOverflowedRecvBuf() { return numOverflowedRecvBuf;} + public int getTotalRecvBufBytes() { return totalRecvBufBytes;} + public int getNumInvalidFrameSize() { return numInvalidFrameSize; } + public long getReadTime() { return readTime; } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java new file mode 100644 index 000000000..15ba9c0fe --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessor.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TProtocol; + +/** + * A processor is a generic object which operates upon an input stream and + * writes to some output stream. + */ +public interface TProcessor { + public void process(TProtocol in, TProtocol out) throws TException; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java new file mode 100644 index 000000000..81933a211 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TProcessorFactory.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.transport.TTransport; + +/** + * The default processor factory just returns a singleton + * instance. + */ +public class TProcessorFactory { + + private final TProcessor processor_; + + public TProcessorFactory(TProcessor processor) { + processor_ = processor; + } + + public TProcessor getProcessor(TTransport trans) { + return processor_; + } + + public boolean isAsyncProcessor() { + return processor_ instanceof TAsyncProcessor; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java new file mode 100644 index 000000000..80002c761 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializable.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TProtocol; + +/** + * Generic base interface for generated Thrift objects. + * + */ +public interface TSerializable { + + /** + * Reads the TObject from the given input protocol. + * + * @param iprot Input protocol + */ + public void read(TProtocol iprot) throws TException; + + /** + * Writes the objects out to the protocol + * + * @param oprot Output protocol + */ + public void write(TProtocol oprot) throws TException; + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java new file mode 100644 index 000000000..4e1ce6129 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TSerializer.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; + +/** + * Generic utility for easily serializing objects into a byte array or Java + * String. + * + */ +public class TSerializer { + + /** + * This is the byte array that data is actually serialized into + */ + private final ByteArrayOutputStream baos_ = new ByteArrayOutputStream(); + + /** + * This transport wraps that byte array + */ + private final TIOStreamTransport transport_ = new TIOStreamTransport(baos_); + + /** + * Internal protocol used for serializing objects. + */ + private TProtocol protocol_; + + /** + * Create a new TSerializer that uses the TBinaryProtocol by default. + */ + public TSerializer() { + this(new TBinaryProtocol.Factory()); + } + + /** + * Create a new TSerializer. It will use the TProtocol specified by the + * factory that is passed in. + * + * @param protocolFactory Factory to create a protocol + */ + public TSerializer(TProtocolFactory protocolFactory) { + protocol_ = protocolFactory.getProtocol(transport_); + } + + /** + * Serialize the Thrift object into a byte array. The process is simple, + * just clear the byte array output, write the object into it, and grab the + * raw bytes. + * + * @param base The object to serialize + * @return Serialized object in byte[] format + */ + public byte[] serialize(TBase base) throws TException { + baos_.reset(); + base.write(protocol_); + return baos_.toByteArray(); + } + + /** + * Serialize the Thrift object into a Java string, using a specified + * character set for encoding. + * + * @param base The object to serialize + * @param charset Valid JVM charset + * @return Serialized object as a String + */ + public String toString(TBase base, String charset) throws TException { + try { + return new String(serialize(base), charset); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset); + } + } + + /** + * Serialize the Thrift object into a Java string, using the default JVM + * charset encoding. + * + * @param base The object to serialize + * @return Serialized object as a String + */ + public String toString(TBase base) throws TException { + return new String(serialize(base)); + } +} + diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java new file mode 100644 index 000000000..00a36ee7f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClient.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; + +/** + * A TServiceClient is used to communicate with a TService implementation + * across protocols and transports. + */ +public abstract class TServiceClient { + public TServiceClient(TProtocol prot) { + this(prot, prot); + } + + public TServiceClient(TProtocol iprot, TProtocol oprot) { + iprot_ = iprot; + oprot_ = oprot; + } + + protected TProtocol iprot_; + protected TProtocol oprot_; + + protected int seqid_; + + /** + * Get the TProtocol being used as the input (read) protocol. + * @return the TProtocol being used as the input (read) protocol. + */ + public TProtocol getInputProtocol() { + return this.iprot_; + } + + /** + * Get the TProtocol being used as the output (write) protocol. + * @return the TProtocol being used as the output (write) protocol. + */ + public TProtocol getOutputProtocol() { + return this.oprot_; + } + + protected void sendBase(String methodName, TBase<?,?> args) throws TException { + sendBase(methodName, args, TMessageType.CALL); + } + + protected void sendBaseOneway(String methodName, TBase<?,?> args) throws TException { + sendBase(methodName, args, TMessageType.ONEWAY); + } + + private void sendBase(String methodName, TBase<?,?> args, byte type) throws TException { + oprot_.writeMessageBegin(new TMessage(methodName, type, ++seqid_)); + args.write(oprot_); + oprot_.writeMessageEnd(); + oprot_.getTransport().flush(); + } + + protected void receiveBase(TBase<?,?> result, String methodName) throws TException { + TMessage msg = iprot_.readMessageBegin(); + if (msg.type == TMessageType.EXCEPTION) { + TApplicationException x = new TApplicationException(); + x.read(iprot_); + iprot_.readMessageEnd(); + throw x; + } + if (msg.seqid != seqid_) { + throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID, + String.format("%s failed: out of sequence response: expected %d but got %d", methodName, seqid_, msg.seqid)); + } + result.read(iprot_); + iprot_.readMessageEnd(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java new file mode 100644 index 000000000..988e65591 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TServiceClientFactory.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TProtocol; + +/** + * A TServiceClientFactory provides a general way to get a TServiceClient + * connected to a remote TService via a protocol. + * @param <T> + */ +public interface TServiceClientFactory<T extends TServiceClient> { + /** + * Get a brand-new T using <i>prot</i> as both the input and output protocol. + * @param prot + * @return A brand-new T using <i>prot</i> as both the input and output protocol. + */ + public T getClient(TProtocol prot); + + /** + * Get a brand new T using the specified input and output protocols. The + * input and output protocols may be the same instance. + * @param iprot + * @param oprot + * @return a brand new T using the specified input and output protocols + */ + public T getClient(TProtocol iprot, TProtocol oprot); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java new file mode 100644 index 000000000..1ef11df49 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/TUnion.java @@ -0,0 +1,279 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolException; +import org.apache.thrift.protocol.TStruct; +import org.apache.thrift.scheme.IScheme; +import org.apache.thrift.scheme.SchemeFactory; +import org.apache.thrift.scheme.StandardScheme; +import org.apache.thrift.scheme.TupleScheme; + +public abstract class TUnion<T extends TUnion<T,F>, F extends TFieldIdEnum> implements TBase<T, F> { + + protected Object value_; + protected F setField_; + + protected TUnion() { + setField_ = null; + value_ = null; + } + + private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); + static { + schemes.put(StandardScheme.class, new TUnionStandardSchemeFactory()); + schemes.put(TupleScheme.class, new TUnionTupleSchemeFactory()); + } + + protected TUnion(F setField, Object value) { + setFieldValue(setField, value); + } + + protected TUnion(TUnion<T, F> other) { + if (!other.getClass().equals(this.getClass())) { + throw new ClassCastException(); + } + setField_ = other.setField_; + value_ = deepCopyObject(other.value_); + } + + private static Object deepCopyObject(Object o) { + if (o instanceof TBase) { + return ((TBase)o).deepCopy(); + } else if (o instanceof ByteBuffer) { + return TBaseHelper.copyBinary((ByteBuffer)o); + } else if (o instanceof List) { + return deepCopyList((List)o); + } else if (o instanceof Set) { + return deepCopySet((Set)o); + } else if (o instanceof Map) { + return deepCopyMap((Map)o); + } else { + return o; + } + } + + private static Map deepCopyMap(Map<Object, Object> map) { + Map copy = new HashMap(map.size()); + for (Map.Entry<Object, Object> entry : map.entrySet()) { + copy.put(deepCopyObject(entry.getKey()), deepCopyObject(entry.getValue())); + } + return copy; + } + + private static Set deepCopySet(Set set) { + Set copy = new HashSet(set.size()); + for (Object o : set) { + copy.add(deepCopyObject(o)); + } + return copy; + } + + private static List deepCopyList(List list) { + List copy = new ArrayList(list.size()); + for (Object o : list) { + copy.add(deepCopyObject(o)); + } + return copy; + } + + public F getSetField() { + return setField_; + } + + public Object getFieldValue() { + return value_; + } + + public Object getFieldValue(F fieldId) { + if (fieldId != setField_) { + throw new IllegalArgumentException("Cannot get the value of field " + fieldId + " because union's set field is " + setField_); + } + + return getFieldValue(); + } + + public Object getFieldValue(int fieldId) { + return getFieldValue(enumForId((short)fieldId)); + } + + public boolean isSet() { + return setField_ != null; + } + + public boolean isSet(F fieldId) { + return setField_ == fieldId; + } + + public boolean isSet(int fieldId) { + return isSet(enumForId((short)fieldId)); + } + + public void read(TProtocol iprot) throws TException { + schemes.get(iprot.getScheme()).getScheme().read(iprot, this); + } + + public void setFieldValue(F fieldId, Object value) { + checkType(fieldId, value); + setField_ = fieldId; + value_ = value; + } + + public void setFieldValue(int fieldId, Object value) { + setFieldValue(enumForId((short)fieldId), value); + } + + public void write(TProtocol oprot) throws TException { + schemes.get(oprot.getScheme()).getScheme().write(oprot, this); + } + + /** + * Implementation should be generated so that we can efficiently type check + * various values. + * @param setField + * @param value + */ + protected abstract void checkType(F setField, Object value) throws ClassCastException; + + /** + * Implementation should be generated to read the right stuff from the wire + * based on the field header. + * @param field + * @return read Object based on the field header, as specified by the argument. + */ + protected abstract Object standardSchemeReadValue(TProtocol iprot, TField field) throws TException; + protected abstract void standardSchemeWriteValue(TProtocol oprot) throws TException; + + protected abstract Object tupleSchemeReadValue(TProtocol iprot, short fieldID) throws TException; + protected abstract void tupleSchemeWriteValue(TProtocol oprot) throws TException; + + protected abstract TStruct getStructDesc(); + + protected abstract TField getFieldDesc(F setField); + + protected abstract F enumForId(short id); + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("<"); + sb.append(this.getClass().getSimpleName()); + sb.append(" "); + + if (getSetField() != null) { + Object v = getFieldValue(); + sb.append(getFieldDesc(getSetField()).name); + sb.append(":"); + if(v instanceof ByteBuffer) { + TBaseHelper.toString((ByteBuffer)v, sb); + } else { + sb.append(v.toString()); + } + } + sb.append(">"); + return sb.toString(); + } + + public final void clear() { + this.setField_ = null; + this.value_ = null; + } + + private static class TUnionStandardSchemeFactory implements SchemeFactory { + public TUnionStandardScheme getScheme() { + return new TUnionStandardScheme(); + } + } + + private static class TUnionStandardScheme extends StandardScheme<TUnion> { + + @Override + public void read(TProtocol iprot, TUnion struct) throws TException { + struct.setField_ = null; + struct.value_ = null; + + iprot.readStructBegin(); + + TField field = iprot.readFieldBegin(); + + struct.value_ = struct.standardSchemeReadValue(iprot, field); + if (struct.value_ != null) { + struct.setField_ = struct.enumForId(field.id); + } + + iprot.readFieldEnd(); + // this is so that we will eat the stop byte. we could put a check here to + // make sure that it actually *is* the stop byte, but it's faster to do it + // this way. + iprot.readFieldBegin(); + iprot.readStructEnd(); + } + + @Override + public void write(TProtocol oprot, TUnion struct) throws TException { + if (struct.getSetField() == null || struct.getFieldValue() == null) { + throw new TProtocolException("Cannot write a TUnion with no set value!"); + } + oprot.writeStructBegin(struct.getStructDesc()); + oprot.writeFieldBegin(struct.getFieldDesc(struct.setField_)); + struct.standardSchemeWriteValue(oprot); + oprot.writeFieldEnd(); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + } + + private static class TUnionTupleSchemeFactory implements SchemeFactory { + public TUnionTupleScheme getScheme() { + return new TUnionTupleScheme(); + } + } + + private static class TUnionTupleScheme extends TupleScheme<TUnion> { + + @Override + public void read(TProtocol iprot, TUnion struct) throws TException { + struct.setField_ = null; + struct.value_ = null; + short fieldID = iprot.readI16(); + struct.value_ = struct.tupleSchemeReadValue(iprot, fieldID); + if (struct.value_ != null) { + struct.setField_ = struct.enumForId(fieldID); + } + } + + @Override + public void write(TProtocol oprot, TUnion struct) throws TException { + if (struct.getSetField() == null || struct.getFieldValue() == null) { + throw new TProtocolException("Cannot write a TUnion with no set value!"); + } + oprot.writeI16(struct.setField_.getThriftFieldId()); + struct.tupleSchemeWriteValue(oprot); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java new file mode 100644 index 000000000..a34b01ebb --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/annotation/Nullable.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation indicating a field, method return, or method parameter may be {@code null}. + * We package our own annotation to avoid a mandatory third-party dependency. + */ +@Retention(RetentionPolicy.CLASS) +public @interface Nullable { + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java new file mode 100644 index 000000000..4ebde0741 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/AsyncMethodCallback.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.async; + +/** + * A handler interface asynchronous clients can implement to receive future + * notice of the results of an asynchronous method call. + * + * @param <T> The return type of the asynchronously invoked method. + */ +public interface AsyncMethodCallback<T> { + /** + * This method will be called when the remote side has completed invoking + * your method call and the result is fully read. For {@code oneway} method + * calls, this method will be called as soon as we have completed writing out + * the request. + * + * @param response The return value of the asynchronously invoked method; + * {@code null} for void methods which includes + * {@code oneway} methods. + */ + void onComplete(T response); + + /** + * This method will be called when there is either an unexpected client-side + * exception like an IOException or else when the remote method raises an + * exception, either declared in the IDL or due to an unexpected server-side + * error. + * + * @param exception The exception encountered processing the the asynchronous + * method call, may be a local exception or an unmarshalled + * remote exception. + */ + void onError(Exception exception); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java new file mode 100644 index 000000000..005018a83 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClient.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.async; + +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TNonblockingTransport; + +public abstract class TAsyncClient { + protected final TProtocolFactory ___protocolFactory; + protected final TNonblockingTransport ___transport; + protected final TAsyncClientManager ___manager; + protected TAsyncMethodCall ___currentMethod; + private Exception ___error; + private long ___timeout; + + public TAsyncClient(TProtocolFactory protocolFactory, TAsyncClientManager manager, TNonblockingTransport transport) { + this(protocolFactory, manager, transport, 0); + } + + public TAsyncClient(TProtocolFactory protocolFactory, TAsyncClientManager manager, TNonblockingTransport transport, long timeout) { + this.___protocolFactory = protocolFactory; + this.___manager = manager; + this.___transport = transport; + this.___timeout = timeout; + } + + public TProtocolFactory getProtocolFactory() { + return ___protocolFactory; + } + + public long getTimeout() { + return ___timeout; + } + + public boolean hasTimeout() { + return ___timeout > 0; + } + + public void setTimeout(long timeout) { + this.___timeout = timeout; + } + + /** + * Is the client in an error state? + * @return If client in an error state? + */ + public boolean hasError() { + return ___error != null; + } + + /** + * Get the client's error - returns null if no error + * @return Get the client's error. <p> returns null if no error + */ + public Exception getError() { + return ___error; + } + + protected void checkReady() { + // Ensure we are not currently executing a method + if (___currentMethod != null) { + throw new IllegalStateException("Client is currently executing another method: " + ___currentMethod.getClass().getName()); + } + + // Ensure we're not in an error state + if (___error != null) { + throw new IllegalStateException("Client has an error!", ___error); + } + } + + /** + * Called by delegate method when finished + */ + protected void onComplete() { + ___currentMethod = null; + } + + /** + * Called by delegate method on error + */ + protected void onError(Exception exception) { + ___transport.close(); + ___currentMethod = null; + ___error = exception; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java new file mode 100644 index 000000000..28feb73d1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientFactory.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.async; + +import org.apache.thrift.transport.TNonblockingTransport; + +public interface TAsyncClientFactory<T extends TAsyncClient> { + public T getAsyncClient(TNonblockingTransport transport); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java new file mode 100644 index 000000000..c07ccd540 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncClientManager.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.async; + +import java.io.IOException; +import java.io.Serializable; +import java.nio.channels.ClosedSelectorException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.Comparator; +import java.util.Iterator; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeoutException; + +import org.apache.thrift.TException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Contains selector thread which transitions method call objects + */ +public class TAsyncClientManager { + private static final Logger LOGGER = LoggerFactory.getLogger(TAsyncClientManager.class.getName()); + + private final SelectThread selectThread; + private final ConcurrentLinkedQueue<TAsyncMethodCall> pendingCalls = new ConcurrentLinkedQueue<TAsyncMethodCall>(); + + public TAsyncClientManager() throws IOException { + this.selectThread = new SelectThread(); + selectThread.start(); + } + + public void call(TAsyncMethodCall method) throws TException { + if (!isRunning()) { + throw new TException("SelectThread is not running"); + } + method.prepareMethodCall(); + pendingCalls.add(method); + selectThread.getSelector().wakeup(); + } + + public void stop() { + selectThread.finish(); + } + + public boolean isRunning() { + return selectThread.isAlive(); + } + + private class SelectThread extends Thread { + private final Selector selector; + private volatile boolean running; + private final TreeSet<TAsyncMethodCall> timeoutWatchSet = new TreeSet<TAsyncMethodCall>(new TAsyncMethodCallTimeoutComparator()); + + public SelectThread() throws IOException { + this.selector = SelectorProvider.provider().openSelector(); + this.running = true; + this.setName("TAsyncClientManager#SelectorThread " + this.getId()); + + // We don't want to hold up the JVM when shutting down + setDaemon(true); + } + + public Selector getSelector() { + return selector; + } + + public void finish() { + running = false; + selector.wakeup(); + } + + public void run() { + while (running) { + try { + try { + if (timeoutWatchSet.size() == 0) { + // No timeouts, so select indefinitely + selector.select(); + } else { + // We have a timeout pending, so calculate the time until then and select appropriately + long nextTimeout = timeoutWatchSet.first().getTimeoutTimestamp(); + long selectTime = nextTimeout - System.currentTimeMillis(); + if (selectTime > 0) { + // Next timeout is in the future, select and wake up then + selector.select(selectTime); + } else { + // Next timeout is now or in past, select immediately so we can time out + selector.selectNow(); + } + } + } catch (IOException e) { + LOGGER.error("Caught IOException in TAsyncClientManager!", e); + } + transitionMethods(); + timeoutMethods(); + startPendingMethods(); + } catch (Exception exception) { + LOGGER.error("Ignoring uncaught exception in SelectThread", exception); + } + } + + try { + selector.close(); + } catch (IOException ex) { + LOGGER.warn("Could not close selector. This may result in leaked resources!", ex); + } + } + + // Transition methods for ready keys + private void transitionMethods() { + try { + Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); + while (keys.hasNext()) { + SelectionKey key = keys.next(); + keys.remove(); + if (!key.isValid()) { + // this can happen if the method call experienced an error and the + // key was cancelled. can also happen if we timeout a method, which + // results in a channel close. + // just skip + continue; + } + TAsyncMethodCall methodCall = (TAsyncMethodCall)key.attachment(); + methodCall.transition(key); + + // If done or error occurred, remove from timeout watch set + if (methodCall.isFinished() || methodCall.getClient().hasError()) { + timeoutWatchSet.remove(methodCall); + } + } + } catch (ClosedSelectorException e) { + LOGGER.error("Caught ClosedSelectorException in TAsyncClientManager!", e); + } + } + + // Timeout any existing method calls + private void timeoutMethods() { + Iterator<TAsyncMethodCall> iterator = timeoutWatchSet.iterator(); + long currentTime = System.currentTimeMillis(); + while (iterator.hasNext()) { + TAsyncMethodCall methodCall = iterator.next(); + if (currentTime >= methodCall.getTimeoutTimestamp()) { + iterator.remove(); + methodCall.onError(new TimeoutException("Operation " + methodCall.getClass() + " timed out after " + (currentTime - methodCall.getStartTime()) + " ms.")); + } else { + break; + } + } + } + + // Start any new calls + private void startPendingMethods() { + TAsyncMethodCall methodCall; + while ((methodCall = pendingCalls.poll()) != null) { + // Catch registration errors. method will catch transition errors and cleanup. + try { + methodCall.start(selector); + + // If timeout specified and first transition went smoothly, add to timeout watch set + TAsyncClient client = methodCall.getClient(); + if (client.hasTimeout() && !client.hasError()) { + timeoutWatchSet.add(methodCall); + } + } catch (Exception exception) { + LOGGER.warn("Caught exception in TAsyncClientManager!", exception); + methodCall.onError(exception); + } + } + } + } + + /** Comparator used in TreeSet */ + private static class TAsyncMethodCallTimeoutComparator implements Comparator<TAsyncMethodCall>, Serializable { + public int compare(TAsyncMethodCall left, TAsyncMethodCall right) { + if (left.getTimeoutTimestamp() == right.getTimeoutTimestamp()) { + return (int)(left.getSequenceId() - right.getSequenceId()); + } else { + return (int)(left.getTimeoutTimestamp() - right.getTimeoutTimestamp()); + } + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java new file mode 100644 index 000000000..3bf1747f4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/async/TAsyncMethodCall.java @@ -0,0 +1,284 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.async; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TMemoryBuffer; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransportException; + +/** + * Encapsulates an async method call. + * <p> + * Need to generate: + * <ul> + * <li>protected abstract void write_args(TProtocol protocol)</li> + * <li>protected abstract T getResult() throws <Exception_1>, <Exception_2>, ...</li> + * </ul> + * + * @param <T> The return type of the encapsulated method call. + */ +public abstract class TAsyncMethodCall<T> { + + private static final int INITIAL_MEMORY_BUFFER_SIZE = 128; + private static AtomicLong sequenceIdCounter = new AtomicLong(0); + + public static enum State { + CONNECTING, + WRITING_REQUEST_SIZE, + WRITING_REQUEST_BODY, + READING_RESPONSE_SIZE, + READING_RESPONSE_BODY, + RESPONSE_READ, + ERROR; + } + + /** + * Next step in the call, initialized by start() + */ + private State state = null; + + protected final TNonblockingTransport transport; + private final TProtocolFactory protocolFactory; + protected final TAsyncClient client; + private final AsyncMethodCallback<T> callback; + private final boolean isOneway; + private long sequenceId; + private final long timeout; + + private ByteBuffer sizeBuffer; + private final byte[] sizeBufferArray = new byte[4]; + private ByteBuffer frameBuffer; + + private long startTime = System.currentTimeMillis(); + + protected TAsyncMethodCall(TAsyncClient client, TProtocolFactory protocolFactory, TNonblockingTransport transport, AsyncMethodCallback<T> callback, boolean isOneway) { + this.transport = transport; + this.callback = callback; + this.protocolFactory = protocolFactory; + this.client = client; + this.isOneway = isOneway; + this.sequenceId = TAsyncMethodCall.sequenceIdCounter.getAndIncrement(); + this.timeout = client.getTimeout(); + } + + protected State getState() { + return state; + } + + protected boolean isFinished() { + return state == State.RESPONSE_READ; + } + + protected long getStartTime() { + return startTime; + } + + protected long getSequenceId() { + return sequenceId; + } + + public TAsyncClient getClient() { + return client; + } + + public boolean hasTimeout() { + return timeout > 0; + } + + public long getTimeoutTimestamp() { + return timeout + startTime; + } + + protected abstract void write_args(TProtocol protocol) throws TException; + + protected abstract T getResult() throws Exception; + + /** + * Initialize buffers. + * @throws TException if buffer initialization fails + */ + protected void prepareMethodCall() throws TException { + TMemoryBuffer memoryBuffer = new TMemoryBuffer(INITIAL_MEMORY_BUFFER_SIZE); + TProtocol protocol = protocolFactory.getProtocol(memoryBuffer); + write_args(protocol); + + int length = memoryBuffer.length(); + frameBuffer = ByteBuffer.wrap(memoryBuffer.getArray(), 0, length); + + TFramedTransport.encodeFrameSize(length, sizeBufferArray); + sizeBuffer = ByteBuffer.wrap(sizeBufferArray); + } + + /** + * Register with selector and start first state, which could be either connecting or writing. + * @throws IOException if register or starting fails + */ + void start(Selector sel) throws IOException { + SelectionKey key; + if (transport.isOpen()) { + state = State.WRITING_REQUEST_SIZE; + key = transport.registerSelector(sel, SelectionKey.OP_WRITE); + } else { + state = State.CONNECTING; + key = transport.registerSelector(sel, SelectionKey.OP_CONNECT); + + // non-blocking connect can complete immediately, + // in which case we should not expect the OP_CONNECT + if (transport.startConnect()) { + registerForFirstWrite(key); + } + } + + key.attach(this); + } + + void registerForFirstWrite(SelectionKey key) throws IOException { + state = State.WRITING_REQUEST_SIZE; + key.interestOps(SelectionKey.OP_WRITE); + } + + protected ByteBuffer getFrameBuffer() { + return frameBuffer; + } + + /** + * Transition to next state, doing whatever work is required. Since this + * method is only called by the selector thread, we can make changes to our + * select interests without worrying about concurrency. + * @param key + */ + void transition(SelectionKey key) { + // Ensure key is valid + if (!key.isValid()) { + key.cancel(); + Exception e = new TTransportException("Selection key not valid!"); + onError(e); + return; + } + + // Transition function + try { + switch (state) { + case CONNECTING: + doConnecting(key); + break; + case WRITING_REQUEST_SIZE: + doWritingRequestSize(); + break; + case WRITING_REQUEST_BODY: + doWritingRequestBody(key); + break; + case READING_RESPONSE_SIZE: + doReadingResponseSize(); + break; + case READING_RESPONSE_BODY: + doReadingResponseBody(key); + break; + default: // RESPONSE_READ, ERROR, or bug + throw new IllegalStateException("Method call in state " + state + + " but selector called transition method. Seems like a bug..."); + } + } catch (Exception e) { + key.cancel(); + key.attach(null); + onError(e); + } + } + + protected void onError(Exception e) { + client.onError(e); + callback.onError(e); + state = State.ERROR; + } + + private void doReadingResponseBody(SelectionKey key) throws IOException { + if (transport.read(frameBuffer) < 0) { + throw new IOException("Read call frame failed"); + } + if (frameBuffer.remaining() == 0) { + cleanUpAndFireCallback(key); + } + } + + private void cleanUpAndFireCallback(SelectionKey key) { + state = State.RESPONSE_READ; + key.interestOps(0); + // this ensures that the TAsyncMethod instance doesn't hang around + key.attach(null); + try { + T result = this.getResult(); + client.onComplete(); + callback.onComplete(result); + } catch (Exception e) { + key.cancel(); + onError(e); + } + } + + private void doReadingResponseSize() throws IOException { + if (transport.read(sizeBuffer) < 0) { + throw new IOException("Read call frame size failed"); + } + if (sizeBuffer.remaining() == 0) { + state = State.READING_RESPONSE_BODY; + frameBuffer = ByteBuffer.allocate(TFramedTransport.decodeFrameSize(sizeBufferArray)); + } + } + + private void doWritingRequestBody(SelectionKey key) throws IOException { + if (transport.write(frameBuffer) < 0) { + throw new IOException("Write call frame failed"); + } + if (frameBuffer.remaining() == 0) { + if (isOneway) { + cleanUpAndFireCallback(key); + } else { + state = State.READING_RESPONSE_SIZE; + sizeBuffer.rewind(); // Prepare to read incoming frame size + key.interestOps(SelectionKey.OP_READ); + } + } + } + + private void doWritingRequestSize() throws IOException { + if (transport.write(sizeBuffer) < 0) { + throw new IOException("Write call frame size failed"); + } + if (sizeBuffer.remaining() == 0) { + state = State.WRITING_REQUEST_BODY; + } + } + + private void doConnecting(SelectionKey key) throws IOException { + if (!key.isConnectable() || !transport.finishConnect()) { + throw new IOException("not connectable or finishConnect returned false after we got an OP_CONNECT"); + } + registerForFirstWrite(key); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java new file mode 100644 index 000000000..be49cb949 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/EnumMetaData.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.TEnum; + +public class EnumMetaData extends FieldValueMetaData { + public final Class<? extends TEnum> enumClass; + + public EnumMetaData(byte type, Class<? extends TEnum> sClass){ + super(type); + this.enumClass = sClass; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java new file mode 100644 index 000000000..445f7e474 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldMetaData.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import java.util.HashMap; +import java.util.Map; +import org.apache.thrift.TBase; +import org.apache.thrift.TFieldIdEnum; + +/** + * This class is used to store meta data about thrift fields. Every field in a + * a struct should have a corresponding instance of this class describing it. + * + */ +public class FieldMetaData implements java.io.Serializable { + public final String fieldName; + public final byte requirementType; + public final FieldValueMetaData valueMetaData; + private static Map<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>> structMap; + + static { + structMap = new HashMap<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>>(); + } + + public FieldMetaData(String name, byte req, FieldValueMetaData vMetaData){ + this.fieldName = name; + this.requirementType = req; + this.valueMetaData = vMetaData; + } + + public static synchronized void addStructMetaDataMap(Class<? extends TBase> sClass, Map<? extends TFieldIdEnum, FieldMetaData> map){ + structMap.put(sClass, map); + } + + /** + * Returns a map with metadata (i.e. instances of FieldMetaData) that + * describe the fields of the given class. + * + * @param sClass The TBase class for which the metadata map is requested + */ + public static synchronized Map<? extends TFieldIdEnum, FieldMetaData> getStructMetaDataMap(Class<? extends TBase> sClass){ + if (!structMap.containsKey(sClass)){ // Load class if it hasn't been loaded + try{ + sClass.newInstance(); + } catch (InstantiationException e){ + throw new RuntimeException("InstantiationException for TBase class: " + sClass.getName() + ", message: " + e.getMessage()); + } catch (IllegalAccessException e){ + throw new RuntimeException("IllegalAccessException for TBase class: " + sClass.getName() + ", message: " + e.getMessage()); + } + } + return structMap.get(sClass); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java new file mode 100644 index 000000000..2180b089b --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/FieldValueMetaData.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.protocol.TType; + +/** + * FieldValueMetaData and collection of subclasses to store metadata about + * the value(s) of a field + */ +public class FieldValueMetaData implements java.io.Serializable { + public final byte type; + + private final boolean isTypedefType; + private final String typedefName; + private final boolean isBinary; + + public FieldValueMetaData(byte type, boolean binary) { + this.type = type; + this.isTypedefType = false; + this.typedefName = null; + this.isBinary = binary; + } + + public FieldValueMetaData(byte type) { + this(type, false); + } + + public FieldValueMetaData(byte type, String typedefName) { + this.type = type; + this.isTypedefType = true; + this.typedefName = typedefName; + this.isBinary = false; + } + + public boolean isTypedef() { + return isTypedefType; + } + + public String getTypedefName() { + return typedefName; + } + + public boolean isStruct() { + return type == TType.STRUCT; + } + + public boolean isContainer() { + return type == TType.LIST || type == TType.MAP || type == TType.SET; + } + + public boolean isBinary() { + return isBinary; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java new file mode 100644 index 000000000..8e7073bf5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/ListMetaData.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +public class ListMetaData extends FieldValueMetaData { + public final FieldValueMetaData elemMetaData; + + public ListMetaData(byte type, FieldValueMetaData eMetaData){ + super(type); + this.elemMetaData = eMetaData; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java new file mode 100644 index 000000000..e7c408c78 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/MapMetaData.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +public class MapMetaData extends FieldValueMetaData { + public final FieldValueMetaData keyMetaData; + public final FieldValueMetaData valueMetaData; + + public MapMetaData(byte type, FieldValueMetaData kMetaData, FieldValueMetaData vMetaData){ + super(type); + this.keyMetaData = kMetaData; + this.valueMetaData = vMetaData; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java new file mode 100644 index 000000000..cf4b96aab --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/SetMetaData.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +public class SetMetaData extends FieldValueMetaData { + public final FieldValueMetaData elemMetaData; + + public SetMetaData(byte type, FieldValueMetaData eMetaData){ + super(type); + this.elemMetaData = eMetaData; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java new file mode 100644 index 000000000..b37d21dab --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/meta_data/StructMetaData.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.TBase; + +public class StructMetaData extends FieldValueMetaData { + public final Class<? extends TBase> structClass; + + public StructMetaData(byte type, Class<? extends TBase> sClass){ + super(type); + this.structClass = sClass; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java new file mode 100644 index 000000000..9e6593074 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/ShortStack.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.util.Arrays; + +/** + * ShortStack is a short-specific Stack implementation written for the express + * purpose of very fast operations on TCompactProtocol's field id stack. This + * implementation performs at least 10x faster than java.util.Stack. + */ +class ShortStack { + + private short[] vector; + + /** Always points to the next location */ + private int top = 0; + + public ShortStack(int initialCapacity) { + vector = new short[initialCapacity]; + } + + public short pop() { + return vector[--top]; + } + + public void push(short pushed) { + if (vector.length == top) { + grow(); + } + vector[top++] = pushed; + } + + private void grow() { + vector = Arrays.copyOf(vector, vector.length << 1); + } + + public void clear() { + top = 0; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("<ShortStack vector:["); + for (int i = 0; i < vector.length; i++) { + boolean isTop = (i == (top - 1)); + short value = vector[i]; + if (i != 0) { + sb.append(' '); + } + if (isTop) { + sb.append(">>").append(value).append("<<"); + } else { + sb.append(value); + } + } + sb.append("]>"); + return sb.toString(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java new file mode 100644 index 000000000..abfc965b7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBase64Utils.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Class for encoding and decoding Base64 data. + * + * This class is kept at package level because the interface does no input + * validation and is therefore too low-level for generalized reuse. + * + * Note also that the encoding does not pad with equal signs , as discussed in + * section 2.2 of the RFC (http://www.faqs.org/rfcs/rfc3548.html). Furthermore, + * bad data encountered when decoding is neither rejected or ignored but simply + * results in bad decoded data -- this is not in compliance with the RFC but is + * done in the interest of performance. + * + */ +class TBase64Utils { + + private static final String ENCODE_TABLE = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /** + * Encode len bytes of data in src at offset srcOff, storing the result into + * dst at offset dstOff. len must be 1, 2, or 3. dst must have at least len+1 + * bytes of space at dstOff. src and dst should not be the same object. This + * method does no validation of the input values in the interest of + * performance. + * + * @param src the source of bytes to encode + * @param srcOff the offset into the source to read the unencoded bytes + * @param len the number of bytes to encode (must be 1, 2, or 3). + * @param dst the destination for the encoding + * @param dstOff the offset into the destination to place the encoded bytes + */ + static final void encode(byte[] src, int srcOff, int len, byte[] dst, + int dstOff) { + dst[dstOff] = (byte)ENCODE_TABLE.charAt((src[srcOff] >> 2) & 0x3F); + if (len == 3) { + dst[dstOff + 1] = + (byte)ENCODE_TABLE.charAt( + ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F)); + dst[dstOff + 2] = + (byte)ENCODE_TABLE.charAt( + ((src[srcOff+1] << 2) & 0x3C) | ((src[srcOff+2] >> 6) & 0x03)); + dst[dstOff + 3] = + (byte)ENCODE_TABLE.charAt(src[srcOff+2] & 0x3F); + } + else if (len == 2) { + dst[dstOff+1] = + (byte)ENCODE_TABLE.charAt( + ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F)); + dst[dstOff + 2] = + (byte)ENCODE_TABLE.charAt((src[srcOff+1] << 2) & 0x3C); + } + else { // len == 1) { + dst[dstOff + 1] = + (byte)ENCODE_TABLE.charAt((src[srcOff] << 4) & 0x30); + } + } + + private static final byte[] DECODE_TABLE = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + }; + + /** + * Decode len bytes of data in src at offset srcOff, storing the result into + * dst at offset dstOff. len must be 2, 3, or 4. dst must have at least len-1 + * bytes of space at dstOff. src and dst may be the same object as long as + * dstoff <= srcOff. This method does no validation of the input values in + * the interest of performance. + * + * @param src the source of bytes to decode + * @param srcOff the offset into the source to read the encoded bytes + * @param len the number of bytes to decode (must be 2, 3, or 4) + * @param dst the destination for the decoding + * @param dstOff the offset into the destination to place the decoded bytes + */ + static final void decode(byte[] src, int srcOff, int len, byte[] dst, + int dstOff) { + dst[dstOff] = (byte) + ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) | + (DECODE_TABLE[src[srcOff+1] & 0x0FF] >> 4)); + if (len > 2) { + dst[dstOff+1] = (byte) + (((DECODE_TABLE[src[srcOff+1] & 0x0FF] << 4) & 0xF0) | + (DECODE_TABLE[src[srcOff+2] & 0x0FF] >> 2)); + if (len > 3) { + dst[dstOff+2] = (byte) + (((DECODE_TABLE[src[srcOff+2] & 0x0FF] << 6) & 0xC0) | + DECODE_TABLE[src[srcOff+3] & 0x0FF]); + } + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java new file mode 100644 index 000000000..7924e2fe6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TBinaryProtocol.java @@ -0,0 +1,457 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * Binary protocol implementation for thrift. + * + */ +public class TBinaryProtocol extends TProtocol { + private static final TStruct ANONYMOUS_STRUCT = new TStruct(); + private static final long NO_LENGTH_LIMIT = -1; + + protected static final int VERSION_MASK = 0xffff0000; + protected static final int VERSION_1 = 0x80010000; + + /** + * The maximum number of bytes to read from the transport for + * variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for + * unlimited. + */ + private final long stringLengthLimit_; + + /** + * The maximum number of elements to read from the network for + * containers (maps, sets, lists), or {@link #NO_LENGTH_LIMIT} for unlimited. + */ + private final long containerLengthLimit_; + + protected boolean strictRead_; + protected boolean strictWrite_; + + private final byte[] inoutTemp = new byte[8]; + + /** + * Factory + */ + public static class Factory implements TProtocolFactory { + protected long stringLengthLimit_; + protected long containerLengthLimit_; + protected boolean strictRead_; + protected boolean strictWrite_; + + public Factory() { + this(false, true); + } + + public Factory(boolean strictRead, boolean strictWrite) { + this(strictRead, strictWrite, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT); + } + + public Factory(long stringLengthLimit, long containerLengthLimit) { + this(false, true, stringLengthLimit, containerLengthLimit); + } + + public Factory(boolean strictRead, boolean strictWrite, long stringLengthLimit, long containerLengthLimit) { + stringLengthLimit_ = stringLengthLimit; + containerLengthLimit_ = containerLengthLimit; + strictRead_ = strictRead; + strictWrite_ = strictWrite; + } + + public TProtocol getProtocol(TTransport trans) { + return new TBinaryProtocol(trans, stringLengthLimit_, containerLengthLimit_, strictRead_, strictWrite_); + } + } + + /** + * Constructor + */ + public TBinaryProtocol(TTransport trans) { + this(trans, false, true); + } + + public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) { + this(trans, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT, strictRead, strictWrite); + } + + public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerLengthLimit) { + this(trans, stringLengthLimit, containerLengthLimit, false, true); + } + + public TBinaryProtocol(TTransport trans, long stringLengthLimit, long containerLengthLimit, boolean strictRead, boolean strictWrite) { + super(trans); + stringLengthLimit_ = stringLengthLimit; + containerLengthLimit_ = containerLengthLimit; + strictRead_ = strictRead; + strictWrite_ = strictWrite; + } + + @Override + public void writeMessageBegin(TMessage message) throws TException { + if (strictWrite_) { + int version = VERSION_1 | message.type; + writeI32(version); + writeString(message.name); + writeI32(message.seqid); + } else { + writeString(message.name); + writeByte(message.type); + writeI32(message.seqid); + } + } + + @Override + public void writeMessageEnd() throws TException {} + + @Override + public void writeStructBegin(TStruct struct) throws TException {} + + @Override + public void writeStructEnd() throws TException {} + + @Override + public void writeFieldBegin(TField field) throws TException { + writeByte(field.type); + writeI16(field.id); + } + + @Override + public void writeFieldEnd() throws TException {} + + @Override + public void writeFieldStop() throws TException { + writeByte(TType.STOP); + } + + @Override + public void writeMapBegin(TMap map) throws TException { + writeByte(map.keyType); + writeByte(map.valueType); + writeI32(map.size); + } + + @Override + public void writeMapEnd() throws TException {} + + @Override + public void writeListBegin(TList list) throws TException { + writeByte(list.elemType); + writeI32(list.size); + } + + @Override + public void writeListEnd() throws TException {} + + @Override + public void writeSetBegin(TSet set) throws TException { + writeByte(set.elemType); + writeI32(set.size); + } + + @Override + public void writeSetEnd() throws TException {} + + @Override + public void writeBool(boolean b) throws TException { + writeByte(b ? (byte)1 : (byte)0); + } + + @Override + public void writeByte(byte b) throws TException { + inoutTemp[0] = b; + trans_.write(inoutTemp, 0, 1); + } + + @Override + public void writeI16(short i16) throws TException { + inoutTemp[0] = (byte)(0xff & (i16 >> 8)); + inoutTemp[1] = (byte)(0xff & (i16)); + trans_.write(inoutTemp, 0, 2); + } + + @Override + public void writeI32(int i32) throws TException { + inoutTemp[0] = (byte)(0xff & (i32 >> 24)); + inoutTemp[1] = (byte)(0xff & (i32 >> 16)); + inoutTemp[2] = (byte)(0xff & (i32 >> 8)); + inoutTemp[3] = (byte)(0xff & (i32)); + trans_.write(inoutTemp, 0, 4); + } + + @Override + public void writeI64(long i64) throws TException { + inoutTemp[0] = (byte)(0xff & (i64 >> 56)); + inoutTemp[1] = (byte)(0xff & (i64 >> 48)); + inoutTemp[2] = (byte)(0xff & (i64 >> 40)); + inoutTemp[3] = (byte)(0xff & (i64 >> 32)); + inoutTemp[4] = (byte)(0xff & (i64 >> 24)); + inoutTemp[5] = (byte)(0xff & (i64 >> 16)); + inoutTemp[6] = (byte)(0xff & (i64 >> 8)); + inoutTemp[7] = (byte)(0xff & (i64)); + trans_.write(inoutTemp, 0, 8); + } + + @Override + public void writeDouble(double dub) throws TException { + writeI64(Double.doubleToLongBits(dub)); + } + + @Override + public void writeString(String str) throws TException { + byte[] dat = str.getBytes(StandardCharsets.UTF_8); + writeI32(dat.length); + trans_.write(dat, 0, dat.length); + } + + @Override + public void writeBinary(ByteBuffer bin) throws TException { + int length = bin.limit() - bin.position(); + writeI32(length); + trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length); + } + + /** + * Reading methods. + */ + + @Override + public TMessage readMessageBegin() throws TException { + int size = readI32(); + if (size < 0) { + int version = size & VERSION_MASK; + if (version != VERSION_1) { + throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin"); + } + return new TMessage(readString(), (byte)(size & 0x000000ff), readI32()); + } else { + if (strictRead_) { + throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?"); + } + return new TMessage(readStringBody(size), readByte(), readI32()); + } + } + + @Override + public void readMessageEnd() throws TException {} + + @Override + public TStruct readStructBegin() throws TException { + return ANONYMOUS_STRUCT; + } + + @Override + public void readStructEnd() throws TException {} + + @Override + public TField readFieldBegin() throws TException { + byte type = readByte(); + short id = type == TType.STOP ? 0 : readI16(); + return new TField("", type, id); + } + + @Override + public void readFieldEnd() throws TException {} + + @Override + public TMap readMapBegin() throws TException { + TMap map = new TMap(readByte(), readByte(), readI32()); + checkContainerReadLength(map.size); + return map; + } + + @Override + public void readMapEnd() throws TException {} + + @Override + public TList readListBegin() throws TException { + TList list = new TList(readByte(), readI32()); + checkContainerReadLength(list.size); + return list; + } + + @Override + public void readListEnd() throws TException {} + + @Override + public TSet readSetBegin() throws TException { + TSet set = new TSet(readByte(), readI32()); + checkContainerReadLength(set.size); + return set; + } + + @Override + public void readSetEnd() throws TException {} + + @Override + public boolean readBool() throws TException { + return (readByte() == 1); + } + + @Override + public byte readByte() throws TException { + if (trans_.getBytesRemainingInBuffer() >= 1) { + byte b = trans_.getBuffer()[trans_.getBufferPosition()]; + trans_.consumeBuffer(1); + return b; + } + readAll(inoutTemp, 0, 1); + return inoutTemp[0]; + } + + @Override + public short readI16() throws TException { + byte[] buf = inoutTemp; + int off = 0; + + if (trans_.getBytesRemainingInBuffer() >= 2) { + buf = trans_.getBuffer(); + off = trans_.getBufferPosition(); + trans_.consumeBuffer(2); + } else { + readAll(inoutTemp, 0, 2); + } + + return + (short) + (((buf[off] & 0xff) << 8) | + ((buf[off+1] & 0xff))); + } + + @Override + public int readI32() throws TException { + byte[] buf = inoutTemp; + int off = 0; + + if (trans_.getBytesRemainingInBuffer() >= 4) { + buf = trans_.getBuffer(); + off = trans_.getBufferPosition(); + trans_.consumeBuffer(4); + } else { + readAll(inoutTemp, 0, 4); + } + return + ((buf[off] & 0xff) << 24) | + ((buf[off+1] & 0xff) << 16) | + ((buf[off+2] & 0xff) << 8) | + ((buf[off+3] & 0xff)); + } + + @Override + public long readI64() throws TException { + byte[] buf = inoutTemp; + int off = 0; + + if (trans_.getBytesRemainingInBuffer() >= 8) { + buf = trans_.getBuffer(); + off = trans_.getBufferPosition(); + trans_.consumeBuffer(8); + } else { + readAll(inoutTemp, 0, 8); + } + + return + ((long)(buf[off] & 0xff) << 56) | + ((long)(buf[off+1] & 0xff) << 48) | + ((long)(buf[off+2] & 0xff) << 40) | + ((long)(buf[off+3] & 0xff) << 32) | + ((long)(buf[off+4] & 0xff) << 24) | + ((long)(buf[off+5] & 0xff) << 16) | + ((long)(buf[off+6] & 0xff) << 8) | + ((long)(buf[off+7] & 0xff)); + } + + @Override + public double readDouble() throws TException { + return Double.longBitsToDouble(readI64()); + } + + @Override + public String readString() throws TException { + int size = readI32(); + + checkStringReadLength(size); + + if (trans_.getBytesRemainingInBuffer() >= size) { + String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), + size, StandardCharsets.UTF_8); + trans_.consumeBuffer(size); + return s; + } + + return readStringBody(size); + } + + public String readStringBody(int size) throws TException { + checkStringReadLength(size); + byte[] buf = new byte[size]; + trans_.readAll(buf, 0, size); + return new String(buf, StandardCharsets.UTF_8); + } + + @Override + public ByteBuffer readBinary() throws TException { + int size = readI32(); + + checkStringReadLength(size); + + if (trans_.getBytesRemainingInBuffer() >= size) { + ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), size); + trans_.consumeBuffer(size); + return bb; + } + + byte[] buf = new byte[size]; + trans_.readAll(buf, 0, size); + return ByteBuffer.wrap(buf); + } + + private void checkStringReadLength(int length) throws TProtocolException { + if (length < 0) { + throw new TProtocolException(TProtocolException.NEGATIVE_SIZE, + "Negative length: " + length); + } + if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) { + throw new TProtocolException(TProtocolException.SIZE_LIMIT, + "Length exceeded max allowed: " + length); + } + } + + private void checkContainerReadLength(int length) throws TProtocolException { + if (length < 0) { + throw new TProtocolException(TProtocolException.NEGATIVE_SIZE, + "Negative length: " + length); + } + if (containerLengthLimit_ != NO_LENGTH_LIMIT && length > containerLengthLimit_) { + throw new TProtocolException(TProtocolException.SIZE_LIMIT, + "Length exceeded max allowed: " + length); + } + } + + private int readAll(byte[] buf, int off, int len) throws TException { + return trans_.readAll(buf, off, len); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java new file mode 100644 index 000000000..ee0586945 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TCompactProtocol.java @@ -0,0 +1,904 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * TCompactProtocol2 is the Java implementation of the compact protocol specified + * in THRIFT-110. The fundamental approach to reducing the overhead of + * structures is a) use variable-length integers all over the place and b) make + * use of unused bits wherever possible. Your savings will obviously vary + * based on the specific makeup of your structs, but in general, the more + * fields, nested structures, short strings and collections, and low-value i32 + * and i64 fields you have, the more benefit you'll see. + */ +public class TCompactProtocol extends TProtocol { + private final static byte[] EMPTY_BYTES = new byte[0]; + private final static ByteBuffer EMPTY_BUFFER = ByteBuffer.wrap(EMPTY_BYTES); + + private final static long NO_LENGTH_LIMIT = -1; + + private final static TStruct ANONYMOUS_STRUCT = new TStruct(""); + private final static TField TSTOP = new TField("", TType.STOP, (short)0); + + private final static byte[] ttypeToCompactType = new byte[16]; + + static { + ttypeToCompactType[TType.STOP] = TType.STOP; + ttypeToCompactType[TType.BOOL] = Types.BOOLEAN_TRUE; + ttypeToCompactType[TType.BYTE] = Types.BYTE; + ttypeToCompactType[TType.I16] = Types.I16; + ttypeToCompactType[TType.I32] = Types.I32; + ttypeToCompactType[TType.I64] = Types.I64; + ttypeToCompactType[TType.DOUBLE] = Types.DOUBLE; + ttypeToCompactType[TType.STRING] = Types.BINARY; + ttypeToCompactType[TType.LIST] = Types.LIST; + ttypeToCompactType[TType.SET] = Types.SET; + ttypeToCompactType[TType.MAP] = Types.MAP; + ttypeToCompactType[TType.STRUCT] = Types.STRUCT; + } + + /** + * TProtocolFactory that produces TCompactProtocols. + */ + public static class Factory implements TProtocolFactory { + private final long stringLengthLimit_; + private final long containerLengthLimit_; + + public Factory() { + this(NO_LENGTH_LIMIT, NO_LENGTH_LIMIT); + } + + public Factory(long stringLengthLimit) { + this(stringLengthLimit, NO_LENGTH_LIMIT); + } + + public Factory(long stringLengthLimit, long containerLengthLimit) { + this.containerLengthLimit_ = containerLengthLimit; + this.stringLengthLimit_ = stringLengthLimit; + } + + public TProtocol getProtocol(TTransport trans) { + return new TCompactProtocol(trans, stringLengthLimit_, containerLengthLimit_); + } + } + + private static final byte PROTOCOL_ID = (byte)0x82; + private static final byte VERSION = 1; + private static final byte VERSION_MASK = 0x1f; // 0001 1111 + private static final byte TYPE_MASK = (byte)0xE0; // 1110 0000 + private static final byte TYPE_BITS = 0x07; // 0000 0111 + private static final int TYPE_SHIFT_AMOUNT = 5; + + /** + * All of the on-wire type codes. + */ + private static class Types { + public static final byte BOOLEAN_TRUE = 0x01; + public static final byte BOOLEAN_FALSE = 0x02; + public static final byte BYTE = 0x03; + public static final byte I16 = 0x04; + public static final byte I32 = 0x05; + public static final byte I64 = 0x06; + public static final byte DOUBLE = 0x07; + public static final byte BINARY = 0x08; + public static final byte LIST = 0x09; + public static final byte SET = 0x0A; + public static final byte MAP = 0x0B; + public static final byte STRUCT = 0x0C; + } + + /** + * Used to keep track of the last field for the current and previous structs, + * so we can do the delta stuff. + */ + private ShortStack lastField_ = new ShortStack(15); + + private short lastFieldId_ = 0; + + /** + * If we encounter a boolean field begin, save the TField here so it can + * have the value incorporated. + */ + private TField booleanField_ = null; + + /** + * If we read a field header, and it's a boolean field, save the boolean + * value here so that readBool can use it. + */ + private Boolean boolValue_ = null; + + /** + * The maximum number of bytes to read from the transport for + * variable-length fields (such as strings or binary) or {@link #NO_LENGTH_LIMIT} for + * unlimited. + */ + private final long stringLengthLimit_; + + /** + * The maximum number of elements to read from the network for + * containers (maps, sets, lists), or {@link #NO_LENGTH_LIMIT} for unlimited. + */ + private final long containerLengthLimit_; + + /** + * Temporary buffer used for various operations that would otherwise require a + * small allocation. + */ + private final byte[] temp = new byte[10]; + + /** + * Create a TCompactProtocol. + * + * @param transport the TTransport object to read from or write to. + * @param stringLengthLimit the maximum number of bytes to read for + * variable-length fields. + * @param containerLengthLimit the maximum number of elements to read + * for containers. + */ + public TCompactProtocol(TTransport transport, long stringLengthLimit, long containerLengthLimit) { + super(transport); + this.stringLengthLimit_ = stringLengthLimit; + this.containerLengthLimit_ = containerLengthLimit; + } + + /** + * Create a TCompactProtocol. + * + * @param transport the TTransport object to read from or write to. + * @param stringLengthLimit the maximum number of bytes to read for + * variable-length fields. + * @deprecated Use constructor specifying both string limit and container limit instead + */ + @Deprecated + public TCompactProtocol(TTransport transport, long stringLengthLimit) { + this(transport, stringLengthLimit, NO_LENGTH_LIMIT); + } + + /** + * Create a TCompactProtocol. + * + * @param transport the TTransport object to read from or write to. + */ + public TCompactProtocol(TTransport transport) { + this(transport, NO_LENGTH_LIMIT, NO_LENGTH_LIMIT); + } + + @Override + public void reset() { + lastField_.clear(); + lastFieldId_ = 0; + } + + // + // Public Writing methods. + // + + /** + * Write a message header to the wire. Compact Protocol messages contain the + * protocol version so we can migrate forwards in the future if need be. + */ + @Override + public void writeMessageBegin(TMessage message) throws TException { + writeByteDirect(PROTOCOL_ID); + writeByteDirect((VERSION & VERSION_MASK) | ((message.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK)); + writeVarint32(message.seqid); + writeString(message.name); + } + + /** + * Write a struct begin. This doesn't actually put anything on the wire. We + * use it as an opportunity to put special placeholder markers on the field + * stack so we can get the field id deltas correct. + */ + @Override + public void writeStructBegin(TStruct struct) throws TException { + lastField_.push(lastFieldId_); + lastFieldId_ = 0; + } + + /** + * Write a struct end. This doesn't actually put anything on the wire. We use + * this as an opportunity to pop the last field from the current struct off + * of the field stack. + */ + public void writeStructEnd() throws TException { + lastFieldId_ = lastField_.pop(); + } + + /** + * Write a field header containing the field id and field type. If the + * difference between the current field id and the last one is small (< 15), + * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the + * field id will follow the type header as a zigzag varint. + */ + public void writeFieldBegin(TField field) throws TException { + if (field.type == TType.BOOL) { + // we want to possibly include the value, so we'll wait. + booleanField_ = field; + } else { + writeFieldBeginInternal(field, (byte)-1); + } + } + + /** + * The workhorse of writeFieldBegin. It has the option of doing a + * 'type override' of the type header. This is used specifically in the + * boolean field case. + */ + private void writeFieldBeginInternal(TField field, byte typeOverride) throws TException { + // short lastField = lastField_.pop(); + + // if there's a type override, use that. + byte typeToWrite = typeOverride == -1 ? getCompactType(field.type) : typeOverride; + + // check if we can use delta encoding for the field id + if (field.id > lastFieldId_ && field.id - lastFieldId_ <= 15) { + // write them together + writeByteDirect((field.id - lastFieldId_) << 4 | typeToWrite); + } else { + // write them separate + writeByteDirect(typeToWrite); + writeI16(field.id); + } + + lastFieldId_ = field.id; + // lastField_.push(field.id); + } + + /** + * Write the STOP symbol so we know there are no more fields in this struct. + */ + public void writeFieldStop() throws TException { + writeByteDirect(TType.STOP); + } + + /** + * Write a map header. If the map is empty, omit the key and value type + * headers, as we don't need any additional information to skip it. + */ + public void writeMapBegin(TMap map) throws TException { + if (map.size == 0) { + writeByteDirect(0); + } else { + writeVarint32(map.size); + writeByteDirect(getCompactType(map.keyType) << 4 | getCompactType(map.valueType)); + } + } + + /** + * Write a list header. + */ + public void writeListBegin(TList list) throws TException { + writeCollectionBegin(list.elemType, list.size); + } + + /** + * Write a set header. + */ + public void writeSetBegin(TSet set) throws TException { + writeCollectionBegin(set.elemType, set.size); + } + + /** + * Write a boolean value. Potentially, this could be a boolean field, in + * which case the field header info isn't written yet. If so, decide what the + * right type header is for the value and then write the field header. + * Otherwise, write a single byte. + */ + public void writeBool(boolean b) throws TException { + if (booleanField_ != null) { + // we haven't written the field header yet + writeFieldBeginInternal(booleanField_, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE); + booleanField_ = null; + } else { + // we're not part of a field, so just write the value. + writeByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE); + } + } + + /** + * Write a byte. Nothing to see here! + */ + public void writeByte(byte b) throws TException { + writeByteDirect(b); + } + + /** + * Write an I16 as a zigzag varint. + */ + public void writeI16(short i16) throws TException { + writeVarint32(intToZigZag(i16)); + } + + /** + * Write an i32 as a zigzag varint. + */ + public void writeI32(int i32) throws TException { + writeVarint32(intToZigZag(i32)); + } + + /** + * Write an i64 as a zigzag varint. + */ + public void writeI64(long i64) throws TException { + writeVarint64(longToZigzag(i64)); + } + + /** + * Write a double to the wire as 8 bytes. + */ + public void writeDouble(double dub) throws TException { + fixedLongToBytes(Double.doubleToLongBits(dub), temp, 0); + trans_.write(temp, 0, 8); + } + + /** + * Write a string to the wire with a varint size preceding. + */ + public void writeString(String str) throws TException { + byte[] bytes = str.getBytes(StandardCharsets.UTF_8); + writeBinary(bytes, 0, bytes.length); + } + + /** + * Write a byte array, using a varint for the size. + */ + public void writeBinary(ByteBuffer bin) throws TException { + int length = bin.limit() - bin.position(); + writeBinary(bin.array(), bin.position() + bin.arrayOffset(), length); + } + + private void writeBinary(byte[] buf, int offset, int length) throws TException { + writeVarint32(length); + trans_.write(buf, offset, length); + } + + // + // These methods are called by structs, but don't actually have any wire + // output or purpose. + // + + public void writeMessageEnd() throws TException {} + public void writeMapEnd() throws TException {} + public void writeListEnd() throws TException {} + public void writeSetEnd() throws TException {} + public void writeFieldEnd() throws TException {} + + // + // Internal writing methods + // + + /** + * Abstract method for writing the start of lists and sets. List and sets on + * the wire differ only by the type indicator. + */ + protected void writeCollectionBegin(byte elemType, int size) throws TException { + if (size <= 14) { + writeByteDirect(size << 4 | getCompactType(elemType)); + } else { + writeByteDirect(0xf0 | getCompactType(elemType)); + writeVarint32(size); + } + } + + /** + * Write an i32 as a varint. Results in 1-5 bytes on the wire. + * TODO: make a permanent buffer like writeVarint64? + */ + private void writeVarint32(int n) throws TException { + int idx = 0; + while (true) { + if ((n & ~0x7F) == 0) { + temp[idx++] = (byte)n; + // writeByteDirect((byte)n); + break; + // return; + } else { + temp[idx++] = (byte)((n & 0x7F) | 0x80); + // writeByteDirect((byte)((n & 0x7F) | 0x80)); + n >>>= 7; + } + } + trans_.write(temp, 0, idx); + } + + /** + * Write an i64 as a varint. Results in 1-10 bytes on the wire. + */ + private void writeVarint64(long n) throws TException { + int idx = 0; + while (true) { + if ((n & ~0x7FL) == 0) { + temp[idx++] = (byte)n; + break; + } else { + temp[idx++] = ((byte)((n & 0x7F) | 0x80)); + n >>>= 7; + } + } + trans_.write(temp, 0, idx); + } + + /** + * Convert l into a zigzag long. This allows negative numbers to be + * represented compactly as a varint. + */ + private long longToZigzag(long l) { + return (l << 1) ^ (l >> 63); + } + + /** + * Convert n into a zigzag int. This allows negative numbers to be + * represented compactly as a varint. + */ + private int intToZigZag(int n) { + return (n << 1) ^ (n >> 31); + } + + /** + * Convert a long into little-endian bytes in buf starting at off and going + * until off+7. + */ + private void fixedLongToBytes(long n, byte[] buf, int off) { + buf[off+0] = (byte)( n & 0xff); + buf[off+1] = (byte)((n >> 8 ) & 0xff); + buf[off+2] = (byte)((n >> 16) & 0xff); + buf[off+3] = (byte)((n >> 24) & 0xff); + buf[off+4] = (byte)((n >> 32) & 0xff); + buf[off+5] = (byte)((n >> 40) & 0xff); + buf[off+6] = (byte)((n >> 48) & 0xff); + buf[off+7] = (byte)((n >> 56) & 0xff); + } + + /** + * Writes a byte without any possibility of all that field header nonsense. + * Used internally by other writing methods that know they need to write a byte. + */ + private void writeByteDirect(byte b) throws TException { + temp[0] = b; + trans_.write(temp, 0, 1); + } + + /** + * Writes a byte without any possibility of all that field header nonsense. + */ + private void writeByteDirect(int n) throws TException { + writeByteDirect((byte)n); + } + + + // + // Reading methods. + // + + /** + * Read a message header. + */ + public TMessage readMessageBegin() throws TException { + byte protocolId = readByte(); + if (protocolId != PROTOCOL_ID) { + throw new TProtocolException("Expected protocol id " + Integer.toHexString(PROTOCOL_ID) + " but got " + Integer.toHexString(protocolId)); + } + byte versionAndType = readByte(); + byte version = (byte)(versionAndType & VERSION_MASK); + if (version != VERSION) { + throw new TProtocolException("Expected version " + VERSION + " but got " + version); + } + byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS); + int seqid = readVarint32(); + String messageName = readString(); + return new TMessage(messageName, type, seqid); + } + + /** + * Read a struct begin. There's nothing on the wire for this, but it is our + * opportunity to push a new struct begin marker onto the field stack. + */ + public TStruct readStructBegin() throws TException { + lastField_.push(lastFieldId_); + lastFieldId_ = 0; + return ANONYMOUS_STRUCT; + } + + /** + * Doesn't actually consume any wire data, just removes the last field for + * this struct from the field stack. + */ + public void readStructEnd() throws TException { + // consume the last field we read off the wire. + lastFieldId_ = lastField_.pop(); + } + + /** + * Read a field header off the wire. + */ + public TField readFieldBegin() throws TException { + byte type = readByte(); + + // if it's a stop, then we can return immediately, as the struct is over. + if (type == TType.STOP) { + return TSTOP; + } + + short fieldId; + + // mask off the 4 MSB of the type header. it could contain a field id delta. + short modifier = (short)((type & 0xf0) >> 4); + if (modifier == 0) { + // not a delta. look ahead for the zigzag varint field id. + fieldId = readI16(); + } else { + // has a delta. add the delta to the last read field id. + fieldId = (short)(lastFieldId_ + modifier); + } + + TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId); + + // if this happens to be a boolean field, the value is encoded in the type + if (isBoolType(type)) { + // save the boolean value in a special instance variable. + boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? Boolean.TRUE : Boolean.FALSE; + } + + // push the new field onto the field stack so we can keep the deltas going. + lastFieldId_ = field.id; + return field; + } + + /** + * Read a map header off the wire. If the size is zero, skip reading the key + * and value type. This means that 0-length maps will yield TMaps without the + * "correct" types. + */ + public TMap readMapBegin() throws TException { + int size = readVarint32(); + checkContainerReadLength(size); + byte keyAndValueType = size == 0 ? 0 : readByte(); + return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size); + } + + /** + * Read a list header off the wire. If the list size is 0-14, the size will + * be packed into the element type header. If it's a longer list, the 4 MSB + * of the element type header will be 0xF, and a varint will follow with the + * true size. + */ + public TList readListBegin() throws TException { + byte size_and_type = readByte(); + int size = (size_and_type >> 4) & 0x0f; + if (size == 15) { + size = readVarint32(); + } + checkContainerReadLength(size); + byte type = getTType(size_and_type); + return new TList(type, size); + } + + /** + * Read a set header off the wire. If the set size is 0-14, the size will + * be packed into the element type header. If it's a longer set, the 4 MSB + * of the element type header will be 0xF, and a varint will follow with the + * true size. + */ + public TSet readSetBegin() throws TException { + return new TSet(readListBegin()); + } + + /** + * Read a boolean off the wire. If this is a boolean field, the value should + * already have been read during readFieldBegin, so we'll just consume the + * pre-stored value. Otherwise, read a byte. + */ + public boolean readBool() throws TException { + if (boolValue_ != null) { + boolean result = boolValue_.booleanValue(); + boolValue_ = null; + return result; + } + return readByte() == Types.BOOLEAN_TRUE; + } + + /** + * Read a single byte off the wire. Nothing interesting here. + */ + public byte readByte() throws TException { + byte b; + if (trans_.getBytesRemainingInBuffer() > 0) { + b = trans_.getBuffer()[trans_.getBufferPosition()]; + trans_.consumeBuffer(1); + } else { + trans_.readAll(temp, 0, 1); + b = temp[0]; + } + return b; + } + + /** + * Read an i16 from the wire as a zigzag varint. + */ + public short readI16() throws TException { + return (short)zigzagToInt(readVarint32()); + } + + /** + * Read an i32 from the wire as a zigzag varint. + */ + public int readI32() throws TException { + return zigzagToInt(readVarint32()); + } + + /** + * Read an i64 from the wire as a zigzag varint. + */ + public long readI64() throws TException { + return zigzagToLong(readVarint64()); + } + + /** + * No magic here - just read a double off the wire. + */ + public double readDouble() throws TException { + trans_.readAll(temp, 0, 8); + return Double.longBitsToDouble(bytesToLong(temp)); + } + + /** + * Reads a byte[] (via readBinary), and then UTF-8 decodes it. + */ + public String readString() throws TException { + int length = readVarint32(); + checkStringReadLength(length); + + if (length == 0) { + return ""; + } + + final String str; + if (trans_.getBytesRemainingInBuffer() >= length) { + str = new String(trans_.getBuffer(), trans_.getBufferPosition(), + length, StandardCharsets.UTF_8); + trans_.consumeBuffer(length); + } else { + str = new String(readBinary(length), StandardCharsets.UTF_8); + } + return str; + } + + /** + * Read a byte[] from the wire. + */ + public ByteBuffer readBinary() throws TException { + int length = readVarint32(); + checkStringReadLength(length); + if (length == 0) return EMPTY_BUFFER; + + if (trans_.getBytesRemainingInBuffer() >= length) { + ByteBuffer bb = ByteBuffer.wrap(trans_.getBuffer(), trans_.getBufferPosition(), length); + trans_.consumeBuffer(length); + return bb; + } + + byte[] buf = new byte[length]; + trans_.readAll(buf, 0, length); + return ByteBuffer.wrap(buf); + } + + /** + * Read a byte[] of a known length from the wire. + */ + private byte[] readBinary(int length) throws TException { + if (length == 0) return EMPTY_BYTES; + + byte[] buf = new byte[length]; + trans_.readAll(buf, 0, length); + return buf; + } + + private void checkStringReadLength(int length) throws TProtocolException { + if (length < 0) { + throw new TProtocolException(TProtocolException.NEGATIVE_SIZE, + "Negative length: " + length); + } + if (stringLengthLimit_ != NO_LENGTH_LIMIT && length > stringLengthLimit_) { + throw new TProtocolException(TProtocolException.SIZE_LIMIT, + "Length exceeded max allowed: " + length); + } + } + + private void checkContainerReadLength(int length) throws TProtocolException { + if (length < 0) { + throw new TProtocolException(TProtocolException.NEGATIVE_SIZE, + "Negative length: " + length); + } + if (containerLengthLimit_ != NO_LENGTH_LIMIT && length > containerLengthLimit_) { + throw new TProtocolException(TProtocolException.SIZE_LIMIT, + "Length exceeded max allowed: " + length); + } + } + + // + // These methods are here for the struct to call, but don't have any wire + // encoding. + // + public void readMessageEnd() throws TException {} + public void readFieldEnd() throws TException {} + public void readMapEnd() throws TException {} + public void readListEnd() throws TException {} + public void readSetEnd() throws TException {} + + // + // Internal reading methods + // + + /** + * Read an i32 from the wire as a varint. The MSB of each byte is set + * if there is another byte to follow. This can read up to 5 bytes. + */ + private int readVarint32() throws TException { + int result = 0; + int shift = 0; + if (trans_.getBytesRemainingInBuffer() >= 5) { + byte[] buf = trans_.getBuffer(); + int pos = trans_.getBufferPosition(); + int off = 0; + while (true) { + byte b = buf[pos+off]; + result |= (int) (b & 0x7f) << shift; + if ((b & 0x80) != 0x80) break; + shift += 7; + off++; + } + trans_.consumeBuffer(off+1); + } else { + while (true) { + byte b = readByte(); + result |= (int) (b & 0x7f) << shift; + if ((b & 0x80) != 0x80) break; + shift += 7; + } + } + return result; + } + + /** + * Read an i64 from the wire as a proper varint. The MSB of each byte is set + * if there is another byte to follow. This can read up to 10 bytes. + */ + private long readVarint64() throws TException { + int shift = 0; + long result = 0; + if (trans_.getBytesRemainingInBuffer() >= 10) { + byte[] buf = trans_.getBuffer(); + int pos = trans_.getBufferPosition(); + int off = 0; + while (true) { + byte b = buf[pos+off]; + result |= (long) (b & 0x7f) << shift; + if ((b & 0x80) != 0x80) break; + shift += 7; + off++; + } + trans_.consumeBuffer(off+1); + } else { + while (true) { + byte b = readByte(); + result |= (long) (b & 0x7f) << shift; + if ((b & 0x80) != 0x80) break; + shift +=7; + } + } + return result; + } + + // + // encoding helpers + // + + /** + * Convert from zigzag int to int. + */ + private int zigzagToInt(int n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * Convert from zigzag long to long. + */ + private long zigzagToLong(long n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * Note that it's important that the mask bytes are long literals, + * otherwise they'll default to ints, and when you shift an int left 56 bits, + * you just get a messed up int. + */ + private long bytesToLong(byte[] bytes) { + return + ((bytes[7] & 0xffL) << 56) | + ((bytes[6] & 0xffL) << 48) | + ((bytes[5] & 0xffL) << 40) | + ((bytes[4] & 0xffL) << 32) | + ((bytes[3] & 0xffL) << 24) | + ((bytes[2] & 0xffL) << 16) | + ((bytes[1] & 0xffL) << 8) | + ((bytes[0] & 0xffL)); + } + + // + // type testing and converting + // + + private boolean isBoolType(byte b) { + int lowerNibble = b & 0x0f; + return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE; + } + + /** + * Given a TCompactProtocol.Types constant, convert it to its corresponding + * TType value. + */ + private byte getTType(byte type) throws TProtocolException { + switch ((byte)(type & 0x0f)) { + case TType.STOP: + return TType.STOP; + case Types.BOOLEAN_FALSE: + case Types.BOOLEAN_TRUE: + return TType.BOOL; + case Types.BYTE: + return TType.BYTE; + case Types.I16: + return TType.I16; + case Types.I32: + return TType.I32; + case Types.I64: + return TType.I64; + case Types.DOUBLE: + return TType.DOUBLE; + case Types.BINARY: + return TType.STRING; + case Types.LIST: + return TType.LIST; + case Types.SET: + return TType.SET; + case Types.MAP: + return TType.MAP; + case Types.STRUCT: + return TType.STRUCT; + default: + throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f)); + } + } + + /** + * Given a TType value, find the appropriate TCompactProtocol.Types constant. + */ + private byte getCompactType(byte ttype) { + return ttypeToCompactType[ttype]; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java new file mode 100644 index 000000000..3872b008f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TField.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates field metadata. + * <p>Two fields are considered equal if they have the same type and id.</p> + */ +public class TField { + public TField() { + this("", TType.STOP, (short)0); + } + + public TField(String n, byte t, short i) { + name = n; + type = t; + id = i; + } + + public final String name; + public final byte type; + public final short id; + + public String toString() { + return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + type; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TField otherField = (TField) obj; + return type == otherField.type && id == otherField.id; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java new file mode 100644 index 000000000..d37c4937f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java @@ -0,0 +1,973 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Stack; + +import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * JSON protocol implementation for thrift. + * + * This is a full-featured protocol supporting write and read. + * + * Please see the C++ class header for a detailed description of the + * protocol's wire format. + * + */ +public class TJSONProtocol extends TProtocol { + + /** + * Factory for JSON protocol objects + */ + public static class Factory implements TProtocolFactory { + protected boolean fieldNamesAsString_ = false; + + public Factory() {} + + public Factory(boolean fieldNamesAsString) { + fieldNamesAsString_ = fieldNamesAsString; + } + + public TProtocol getProtocol(TTransport trans) { + return new TJSONProtocol(trans, fieldNamesAsString_); + } + + } + + private static final byte[] COMMA = new byte[] {','}; + private static final byte[] COLON = new byte[] {':'}; + private static final byte[] LBRACE = new byte[] {'{'}; + private static final byte[] RBRACE = new byte[] {'}'}; + private static final byte[] LBRACKET = new byte[] {'['}; + private static final byte[] RBRACKET = new byte[] {']'}; + private static final byte[] QUOTE = new byte[] {'"'}; + private static final byte[] BACKSLASH = new byte[] {'\\'}; + private static final byte[] ZERO = new byte[] {'0'}; + + private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'}; + + private static final long VERSION = 1; + + private static final byte[] JSON_CHAR_TABLE = { + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + }; + + private static final String ESCAPE_CHARS = "\"\\/bfnrt"; + + private static final byte[] ESCAPE_CHAR_VALS = { + '"', '\\', '/', '\b', '\f', '\n', '\r', '\t', + }; + + private static final int DEF_STRING_SIZE = 16; + + private static final byte[] NAME_BOOL = new byte[] {'t', 'f'}; + private static final byte[] NAME_BYTE = new byte[] {'i','8'}; + private static final byte[] NAME_I16 = new byte[] {'i','1','6'}; + private static final byte[] NAME_I32 = new byte[] {'i','3','2'}; + private static final byte[] NAME_I64 = new byte[] {'i','6','4'}; + private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'}; + private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'}; + private static final byte[] NAME_STRING = new byte[] {'s','t','r'}; + private static final byte[] NAME_MAP = new byte[] {'m','a','p'}; + private static final byte[] NAME_LIST = new byte[] {'l','s','t'}; + private static final byte[] NAME_SET = new byte[] {'s','e','t'}; + + private static final TStruct ANONYMOUS_STRUCT = new TStruct(); + + private static final byte[] getTypeNameForTypeID(byte typeID) + throws TException { + switch (typeID) { + case TType.BOOL: + return NAME_BOOL; + case TType.BYTE: + return NAME_BYTE; + case TType.I16: + return NAME_I16; + case TType.I32: + return NAME_I32; + case TType.I64: + return NAME_I64; + case TType.DOUBLE: + return NAME_DOUBLE; + case TType.STRING: + return NAME_STRING; + case TType.STRUCT: + return NAME_STRUCT; + case TType.MAP: + return NAME_MAP; + case TType.SET: + return NAME_SET; + case TType.LIST: + return NAME_LIST; + default: + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Unrecognized type"); + } + } + + private static final byte getTypeIDForTypeName(byte[] name) + throws TException { + byte result = TType.STOP; + if (name.length > 1) { + switch (name[0]) { + case 'd': + result = TType.DOUBLE; + break; + case 'i': + switch (name[1]) { + case '8': + result = TType.BYTE; + break; + case '1': + result = TType.I16; + break; + case '3': + result = TType.I32; + break; + case '6': + result = TType.I64; + break; + } + break; + case 'l': + result = TType.LIST; + break; + case 'm': + result = TType.MAP; + break; + case 'r': + result = TType.STRUCT; + break; + case 's': + if (name[1] == 't') { + result = TType.STRING; + } + else if (name[1] == 'e') { + result = TType.SET; + } + break; + case 't': + result = TType.BOOL; + break; + } + } + if (result == TType.STOP) { + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Unrecognized type"); + } + return result; + } + + // Base class for tracking JSON contexts that may require inserting/reading + // additional JSON syntax characters + // This base context does nothing. + protected class JSONBaseContext { + protected void write() throws TException {} + + protected void read() throws TException {} + + protected boolean escapeNum() { return false; } + } + + // Context for JSON lists. Will insert/read commas before each item except + // for the first one + protected class JSONListContext extends JSONBaseContext { + private boolean first_ = true; + + @Override + protected void write() throws TException { + if (first_) { + first_ = false; + } else { + trans_.write(COMMA); + } + } + + @Override + protected void read() throws TException { + if (first_) { + first_ = false; + } else { + readJSONSyntaxChar(COMMA); + } + } + } + + // Context for JSON records. Will insert/read colons before the value portion + // of each record pair, and commas before each key except the first. In + // addition, will indicate that numbers in the key position need to be + // escaped in quotes (since JSON keys must be strings). + protected class JSONPairContext extends JSONBaseContext { + private boolean first_ = true; + private boolean colon_ = true; + + @Override + protected void write() throws TException { + if (first_) { + first_ = false; + colon_ = true; + } else { + trans_.write(colon_ ? COLON : COMMA); + colon_ = !colon_; + } + } + + @Override + protected void read() throws TException { + if (first_) { + first_ = false; + colon_ = true; + } else { + readJSONSyntaxChar(colon_ ? COLON : COMMA); + colon_ = !colon_; + } + } + + @Override + protected boolean escapeNum() { + return colon_; + } + } + + // Holds up to one byte from the transport + protected class LookaheadReader { + + private boolean hasData_; + private byte[] data_ = new byte[1]; + + // Return and consume the next byte to be read, either taking it from the + // data buffer if present or getting it from the transport otherwise. + protected byte read() throws TException { + if (hasData_) { + hasData_ = false; + } + else { + trans_.readAll(data_, 0, 1); + } + return data_[0]; + } + + // Return the next byte to be read without consuming, filling the data + // buffer if it has not been filled already. + protected byte peek() throws TException { + if (!hasData_) { + trans_.readAll(data_, 0, 1); + } + hasData_ = true; + return data_[0]; + } + } + + // Stack of nested contexts that we may be in + private Stack<JSONBaseContext> contextStack_ = new Stack<JSONBaseContext>(); + + // Current context that we are in + private JSONBaseContext context_ = new JSONBaseContext(); + + // Reader that manages a 1-byte buffer + private LookaheadReader reader_ = new LookaheadReader(); + + // Write out the TField names as a string instead of the default integer value + private boolean fieldNamesAsString_ = false; + + // Push a new JSON context onto the stack. + private void pushContext(JSONBaseContext c) { + contextStack_.push(context_); + context_ = c; + } + + // Pop the last JSON context off the stack + private void popContext() { + context_ = contextStack_.pop(); + } + + // Reset the context stack to its initial state + private void resetContext() { + while (!contextStack_.isEmpty()) { + popContext(); + } + } + + /** + * Constructor + */ + public TJSONProtocol(TTransport trans) { + super(trans); + } + + public TJSONProtocol(TTransport trans, boolean fieldNamesAsString) { + super(trans); + fieldNamesAsString_ = fieldNamesAsString; + } + + @Override + public void reset() { + contextStack_.clear(); + context_ = new JSONBaseContext(); + reader_ = new LookaheadReader(); + } + + // Temporary buffer used by several methods + private byte[] tmpbuf_ = new byte[4]; + + // Read a byte that must match b[0]; otherwise an exception is thrown. + // Marked protected to avoid synthetic accessor in JSONListContext.read + // and JSONPairContext.read + protected void readJSONSyntaxChar(byte[] b) throws TException { + byte ch = reader_.read(); + if (ch != b[0]) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Unexpected character:" + (char)ch); + } + } + + // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its + // corresponding hex value + private static final byte hexVal(byte ch) throws TException { + if ((ch >= '0') && (ch <= '9')) { + return (byte)((char)ch - '0'); + } + else if ((ch >= 'a') && (ch <= 'f')) { + return (byte)((char)ch - 'a' + 10); + } + else { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected hex character"); + } + } + + // Convert a byte containing a hex value to its corresponding hex character + private static final byte hexChar(byte val) { + val &= 0x0F; + if (val < 10) { + return (byte)((char)val + '0'); + } + else { + return (byte)((char)(val - 10) + 'a'); + } + } + + // Write the bytes in array buf as a JSON characters, escaping as needed + private void writeJSONString(byte[] b) throws TException { + context_.write(); + trans_.write(QUOTE); + int len = b.length; + for (int i = 0; i < len; i++) { + if ((b[i] & 0x00FF) >= 0x30) { + if (b[i] == BACKSLASH[0]) { + trans_.write(BACKSLASH); + trans_.write(BACKSLASH); + } + else { + trans_.write(b, i, 1); + } + } + else { + tmpbuf_[0] = JSON_CHAR_TABLE[b[i]]; + if (tmpbuf_[0] == 1) { + trans_.write(b, i, 1); + } + else if (tmpbuf_[0] > 1) { + trans_.write(BACKSLASH); + trans_.write(tmpbuf_, 0, 1); + } + else { + trans_.write(ESCSEQ); + tmpbuf_[0] = hexChar((byte)(b[i] >> 4)); + tmpbuf_[1] = hexChar(b[i]); + trans_.write(tmpbuf_, 0, 2); + } + } + } + trans_.write(QUOTE); + } + + // Write out number as a JSON value. If the context dictates so, it will be + // wrapped in quotes to output as a JSON string. + private void writeJSONInteger(long num) throws TException { + context_.write(); + String str = Long.toString(num); + boolean escapeNum = context_.escapeNum(); + if (escapeNum) { + trans_.write(QUOTE); + } + byte[] buf = str.getBytes(StandardCharsets.UTF_8); + trans_.write(buf); + if (escapeNum) { + trans_.write(QUOTE); + } + } + + // Write out a double as a JSON value. If it is NaN or infinity or if the + // context dictates escaping, write out as JSON string. + private void writeJSONDouble(double num) throws TException { + context_.write(); + String str = Double.toString(num); + boolean special = false; + switch (str.charAt(0)) { + case 'N': // NaN + case 'I': // Infinity + special = true; + break; + case '-': + if (str.charAt(1) == 'I') { // -Infinity + special = true; + } + break; + default: + break; + } + + boolean escapeNum = special || context_.escapeNum(); + if (escapeNum) { + trans_.write(QUOTE); + } + byte[] b = str.getBytes(StandardCharsets.UTF_8); + trans_.write(b, 0, b.length); + if (escapeNum) { + trans_.write(QUOTE); + } + } + + // Write out contents of byte array b as a JSON string with base-64 encoded + // data + private void writeJSONBase64(byte[] b, int offset, int length) throws TException { + context_.write(); + trans_.write(QUOTE); + int len = length; + int off = offset; + while (len >= 3) { + // Encode 3 bytes at a time + TBase64Utils.encode(b, off, 3, tmpbuf_, 0); + trans_.write(tmpbuf_, 0, 4); + off += 3; + len -= 3; + } + if (len > 0) { + // Encode remainder + TBase64Utils.encode(b, off, len, tmpbuf_, 0); + trans_.write(tmpbuf_, 0, len + 1); + } + trans_.write(QUOTE); + } + + private void writeJSONObjectStart() throws TException { + context_.write(); + trans_.write(LBRACE); + pushContext(new JSONPairContext()); + } + + private void writeJSONObjectEnd() throws TException { + popContext(); + trans_.write(RBRACE); + } + + private void writeJSONArrayStart() throws TException { + context_.write(); + trans_.write(LBRACKET); + pushContext(new JSONListContext()); + } + + private void writeJSONArrayEnd() throws TException { + popContext(); + trans_.write(RBRACKET); + } + + @Override + public void writeMessageBegin(TMessage message) throws TException { + resetContext(); // THRIFT-3743 + writeJSONArrayStart(); + writeJSONInteger(VERSION); + byte[] b = message.name.getBytes(StandardCharsets.UTF_8); + writeJSONString(b); + writeJSONInteger(message.type); + writeJSONInteger(message.seqid); + } + + @Override + public void writeMessageEnd() throws TException { + writeJSONArrayEnd(); + } + + @Override + public void writeStructBegin(TStruct struct) throws TException { + writeJSONObjectStart(); + } + + @Override + public void writeStructEnd() throws TException { + writeJSONObjectEnd(); + } + + @Override + public void writeFieldBegin(TField field) throws TException { + if (fieldNamesAsString_) { + writeString(field.name); + } else { + writeJSONInteger(field.id); + } + writeJSONObjectStart(); + writeJSONString(getTypeNameForTypeID(field.type)); + } + + @Override + public void writeFieldEnd() throws TException { + writeJSONObjectEnd(); + } + + @Override + public void writeFieldStop() {} + + @Override + public void writeMapBegin(TMap map) throws TException { + writeJSONArrayStart(); + writeJSONString(getTypeNameForTypeID(map.keyType)); + writeJSONString(getTypeNameForTypeID(map.valueType)); + writeJSONInteger(map.size); + writeJSONObjectStart(); + } + + @Override + public void writeMapEnd() throws TException { + writeJSONObjectEnd(); + writeJSONArrayEnd(); + } + + @Override + public void writeListBegin(TList list) throws TException { + writeJSONArrayStart(); + writeJSONString(getTypeNameForTypeID(list.elemType)); + writeJSONInteger(list.size); + } + + @Override + public void writeListEnd() throws TException { + writeJSONArrayEnd(); + } + + @Override + public void writeSetBegin(TSet set) throws TException { + writeJSONArrayStart(); + writeJSONString(getTypeNameForTypeID(set.elemType)); + writeJSONInteger(set.size); + } + + @Override + public void writeSetEnd() throws TException { + writeJSONArrayEnd(); + } + + @Override + public void writeBool(boolean b) throws TException { + writeJSONInteger(b ? (long)1 : (long)0); + } + + @Override + public void writeByte(byte b) throws TException { + writeJSONInteger((long)b); + } + + @Override + public void writeI16(short i16) throws TException { + writeJSONInteger((long)i16); + } + + @Override + public void writeI32(int i32) throws TException { + writeJSONInteger((long)i32); + } + + @Override + public void writeI64(long i64) throws TException { + writeJSONInteger(i64); + } + + @Override + public void writeDouble(double dub) throws TException { + writeJSONDouble(dub); + } + + @Override + public void writeString(String str) throws TException { + byte[] b = str.getBytes(StandardCharsets.UTF_8); + writeJSONString(b); + } + + @Override + public void writeBinary(ByteBuffer bin) throws TException { + writeJSONBase64(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset()); + } + + /** + * Reading methods. + */ + + // Read in a JSON string, unescaping as appropriate.. Skip reading from the + // context if skipContext is true. + private TByteArrayOutputStream readJSONString(boolean skipContext) + throws TException { + TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE); + ArrayList<Character> codeunits = new ArrayList<Character>(); + if (!skipContext) { + context_.read(); + } + readJSONSyntaxChar(QUOTE); + while (true) { + byte ch = reader_.read(); + if (ch == QUOTE[0]) { + break; + } + if (ch == ESCSEQ[0]) { + ch = reader_.read(); + if (ch == ESCSEQ[1]) { + trans_.readAll(tmpbuf_, 0, 4); + short cu = (short)( + ((short)hexVal(tmpbuf_[0]) << 12) + + ((short)hexVal(tmpbuf_[1]) << 8) + + ((short)hexVal(tmpbuf_[2]) << 4) + + (short)hexVal(tmpbuf_[3])); + try { + if (Character.isHighSurrogate((char)cu)) { + if (codeunits.size() > 0) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected low surrogate char"); + } + codeunits.add((char)cu); + } + else if (Character.isLowSurrogate((char)cu)) { + if (codeunits.size() == 0) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected high surrogate char"); + } + + codeunits.add((char)cu); + arr.write( + (new String(new int[] { codeunits.get(0), codeunits.get(1) }, + 0, 2)).getBytes(StandardCharsets.UTF_8)); + codeunits.clear(); + } + else { + arr.write((new String(new int[] { cu }, 0, 1)) + .getBytes(StandardCharsets.UTF_8)); + } + continue; + } catch (IOException ex) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Invalid unicode sequence"); + } + } + else { + int off = ESCAPE_CHARS.indexOf(ch); + if (off == -1) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected control char"); + } + ch = ESCAPE_CHAR_VALS[off]; + } + } + arr.write(ch); + } + return arr; + } + + // Return true if the given byte could be a valid part of a JSON number. + private boolean isJSONNumeric(byte b) { + switch (b) { + case '+': + case '-': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'E': + case 'e': + return true; + } + return false; + } + + // Read in a sequence of characters that are all valid in JSON numbers. Does + // not do a complete regex check to validate that this is actually a number. + private String readJSONNumericChars() throws TException { + StringBuilder strbld = new StringBuilder(); + while (true) { + byte ch = reader_.peek(); + if (!isJSONNumeric(ch)) { + break; + } + strbld.append((char)reader_.read()); + } + return strbld.toString(); + } + + // Read in a JSON number. If the context dictates, read in enclosing quotes. + private long readJSONInteger() throws TException { + context_.read(); + if (context_.escapeNum()) { + readJSONSyntaxChar(QUOTE); + } + String str = readJSONNumericChars(); + if (context_.escapeNum()) { + readJSONSyntaxChar(QUOTE); + } + try { + return Long.valueOf(str); + } + catch (NumberFormatException ex) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data"); + } + } + + // Read in a JSON double value. Throw if the value is not wrapped in quotes + // when expected or if wrapped in quotes when not expected. + private double readJSONDouble() throws TException { + context_.read(); + if (reader_.peek() == QUOTE[0]) { + TByteArrayOutputStream arr = readJSONString(true); + double dub = Double.valueOf(arr.toString(StandardCharsets.UTF_8)); + if (!context_.escapeNum() && !Double.isNaN(dub) + && !Double.isInfinite(dub)) { + // Throw exception -- we should not be in a string in this case + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Numeric data unexpectedly quoted"); + } + return dub; + } + else { + if (context_.escapeNum()) { + // This will throw - we should have had a quote if escapeNum == true + readJSONSyntaxChar(QUOTE); + } + try { + return Double.valueOf(readJSONNumericChars()); + } + catch (NumberFormatException ex) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data"); + } + } + } + + // Read in a JSON string containing base-64 encoded data and decode it. + private byte[] readJSONBase64() throws TException { + TByteArrayOutputStream arr = readJSONString(false); + byte[] b = arr.get(); + int len = arr.len(); + int off = 0; + int size = 0; + // Ignore padding + int bound = len >= 2 ? len - 2 : 0; + for (int i = len - 1; i >= bound && b[i] == '='; --i) { + --len; + } + while (len >= 4) { + // Decode 4 bytes at a time + TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place + off += 4; + len -= 4; + size += 3; + } + // Don't decode if we hit the end or got a single leftover byte (invalid + // base64 but legal for skip of regular string type) + if (len > 1) { + // Decode remainder + TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place + size += len - 1; + } + // Sadly we must copy the byte[] (any way around this?) + byte [] result = new byte[size]; + System.arraycopy(b, 0, result, 0, size); + return result; + } + + private void readJSONObjectStart() throws TException { + context_.read(); + readJSONSyntaxChar(LBRACE); + pushContext(new JSONPairContext()); + } + + private void readJSONObjectEnd() throws TException { + readJSONSyntaxChar(RBRACE); + popContext(); + } + + private void readJSONArrayStart() throws TException { + context_.read(); + readJSONSyntaxChar(LBRACKET); + pushContext(new JSONListContext()); + } + + private void readJSONArrayEnd() throws TException { + readJSONSyntaxChar(RBRACKET); + popContext(); + } + + @Override + public TMessage readMessageBegin() throws TException { + resetContext(); // THRIFT-3743 + readJSONArrayStart(); + if (readJSONInteger() != VERSION) { + throw new TProtocolException(TProtocolException.BAD_VERSION, + "Message contained bad version."); + } + String name = readJSONString(false).toString(StandardCharsets.UTF_8); + byte type = (byte) readJSONInteger(); + int seqid = (int) readJSONInteger(); + return new TMessage(name, type, seqid); + } + + @Override + public void readMessageEnd() throws TException { + readJSONArrayEnd(); + } + + @Override + public TStruct readStructBegin() throws TException { + readJSONObjectStart(); + return ANONYMOUS_STRUCT; + } + + @Override + public void readStructEnd() throws TException { + readJSONObjectEnd(); + } + + @Override + public TField readFieldBegin() throws TException { + byte ch = reader_.peek(); + byte type; + short id = 0; + if (ch == RBRACE[0]) { + type = TType.STOP; + } + else { + id = (short) readJSONInteger(); + readJSONObjectStart(); + type = getTypeIDForTypeName(readJSONString(false).get()); + } + return new TField("", type, id); + } + + @Override + public void readFieldEnd() throws TException { + readJSONObjectEnd(); + } + + @Override + public TMap readMapBegin() throws TException { + readJSONArrayStart(); + byte keyType = getTypeIDForTypeName(readJSONString(false).get()); + byte valueType = getTypeIDForTypeName(readJSONString(false).get()); + int size = (int)readJSONInteger(); + readJSONObjectStart(); + return new TMap(keyType, valueType, size); + } + + @Override + public void readMapEnd() throws TException { + readJSONObjectEnd(); + readJSONArrayEnd(); + } + + @Override + public TList readListBegin() throws TException { + readJSONArrayStart(); + byte elemType = getTypeIDForTypeName(readJSONString(false).get()); + int size = (int)readJSONInteger(); + return new TList(elemType, size); + } + + @Override + public void readListEnd() throws TException { + readJSONArrayEnd(); + } + + @Override + public TSet readSetBegin() throws TException { + readJSONArrayStart(); + byte elemType = getTypeIDForTypeName(readJSONString(false).get()); + int size = (int)readJSONInteger(); + return new TSet(elemType, size); + } + + @Override + public void readSetEnd() throws TException { + readJSONArrayEnd(); + } + + @Override + public boolean readBool() throws TException { + return (readJSONInteger() == 0 ? false : true); + } + + @Override + public byte readByte() throws TException { + return (byte) readJSONInteger(); + } + + @Override + public short readI16() throws TException { + return (short) readJSONInteger(); + } + + @Override + public int readI32() throws TException { + return (int) readJSONInteger(); + } + + @Override + public long readI64() throws TException { + return (long) readJSONInteger(); + } + + @Override + public double readDouble() throws TException { + return readJSONDouble(); + } + + @Override + public String readString() throws TException { + return readJSONString(false).toString(StandardCharsets.UTF_8); + } + + @Override + public ByteBuffer readBinary() throws TException { + return ByteBuffer.wrap(readJSONBase64()); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java new file mode 100644 index 000000000..0d36e83d9 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TList.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates list metadata. + * + */ +public final class TList { + public TList() { + this(TType.STOP, 0); + } + + public TList(byte t, int s) { + elemType = t; + size = s; + } + + public final byte elemType; + public final int size; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java new file mode 100644 index 000000000..20881f7ac --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMap.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates map metadata. + * + */ +public final class TMap { + public TMap() { + this(TType.STOP, TType.STOP, 0); + } + + public TMap(byte k, byte v, int s) { + keyType = k; + valueType = v; + size = s; + } + + public final byte keyType; + public final byte valueType; + public final int size; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java new file mode 100644 index 000000000..f13b8ca50 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessage.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates struct metadata. + * + */ +public final class TMessage { + public TMessage() { + this("", TType.STOP, 0); + } + + public TMessage(String n, byte t, int s) { + name = n; + type = t; + seqid = s; + } + + public final String name; + public final byte type; + public final int seqid; + + @Override + public String toString() { + return "<TMessage name:'" + name + "' type: " + type + " seqid:" + seqid + ">"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + seqid; + result = prime * result + type; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + TMessage other = (TMessage) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (seqid != other.seqid) + return false; + if (type != other.type) + return false; + return true; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java new file mode 100644 index 000000000..aa3f93177 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMessageType.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Message type constants in the Thrift protocol. + * + */ +public final class TMessageType { + public static final byte CALL = 1; + public static final byte REPLY = 2; + public static final byte EXCEPTION = 3; + public static final byte ONEWAY = 4; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java new file mode 100644 index 000000000..0ea566ba6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TMultiplexedProtocol.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; + +/** + * <code>TMultiplexedProtocol</code> is a protocol-independent concrete decorator + * that allows a Thrift client to communicate with a multiplexing Thrift server, + * by prepending the service name to the function name during function calls. + * + * <p>NOTE: THIS IS NOT USED BY SERVERS. On the server, use {@link org.apache.thrift.TMultiplexedProcessor TMultiplexedProcessor} to handle requests + * from a multiplexing client. + * + * <p>This example uses a single socket transport to invoke two services: + * + * <pre> + * {@code + * TSocket transport = new TSocket("localhost", 9090); + * transport.open(); + * + * TBinaryProtocol protocol = new TBinaryProtocol(transport); + * + * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator"); + * Calculator.Client service = new Calculator.Client(mp); + * + * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport"); + * WeatherReport.Client service2 = new WeatherReport.Client(mp2); + * + * System.out.println(service.add(2,2)); + * System.out.println(service2.getTemperature()); + * } + * </pre> + * + * @see org.apache.thrift.protocol.TProtocolDecorator + */ +public class TMultiplexedProtocol extends TProtocolDecorator { + + /** Used to delimit the service name from the function name */ + public static final String SEPARATOR = ":"; + + private final String SERVICE_NAME; + + /** + * Wrap the specified protocol, allowing it to be used to communicate with a + * multiplexing server. The <code>serviceName</code> is required as it is + * prepended to the message header so that the multiplexing server can broker + * the function call to the proper service. + * + * @param protocol Your communication protocol of choice, e.g. <code>TBinaryProtocol</code>. + * @param serviceName The service name of the service communicating via this protocol. + */ + public TMultiplexedProtocol(TProtocol protocol, String serviceName) { + super(protocol); + SERVICE_NAME = serviceName; + } + + /** + * Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR. + * + * @param tMessage The original message. + * @throws TException Passed through from wrapped <code>TProtocol</code> instance. + */ + @Override + public void writeMessageBegin(TMessage tMessage) throws TException { + if (tMessage.type == TMessageType.CALL || tMessage.type == TMessageType.ONEWAY) { + super.writeMessageBegin(new TMessage( + SERVICE_NAME + SEPARATOR + tMessage.name, + tMessage.type, + tMessage.seqid + )); + } else { + super.writeMessageBegin(tMessage); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java new file mode 100644 index 000000000..0e96368d4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocol.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.nio.ByteBuffer; + +import org.apache.thrift.TException; +import org.apache.thrift.scheme.IScheme; +import org.apache.thrift.scheme.StandardScheme; +import org.apache.thrift.transport.TTransport; + +/** + * Protocol interface definition. + * + */ +public abstract class TProtocol { + + /** + * Prevent direct instantiation + */ + @SuppressWarnings("unused") + private TProtocol() {} + + /** + * Transport + */ + protected TTransport trans_; + + /** + * Constructor + */ + protected TProtocol(TTransport trans) { + trans_ = trans; + } + + /** + * Transport accessor + */ + public TTransport getTransport() { + return trans_; + } + + /** + * Writing methods. + */ + + public abstract void writeMessageBegin(TMessage message) throws TException; + + public abstract void writeMessageEnd() throws TException; + + public abstract void writeStructBegin(TStruct struct) throws TException; + + public abstract void writeStructEnd() throws TException; + + public abstract void writeFieldBegin(TField field) throws TException; + + public abstract void writeFieldEnd() throws TException; + + public abstract void writeFieldStop() throws TException; + + public abstract void writeMapBegin(TMap map) throws TException; + + public abstract void writeMapEnd() throws TException; + + public abstract void writeListBegin(TList list) throws TException; + + public abstract void writeListEnd() throws TException; + + public abstract void writeSetBegin(TSet set) throws TException; + + public abstract void writeSetEnd() throws TException; + + public abstract void writeBool(boolean b) throws TException; + + public abstract void writeByte(byte b) throws TException; + + public abstract void writeI16(short i16) throws TException; + + public abstract void writeI32(int i32) throws TException; + + public abstract void writeI64(long i64) throws TException; + + public abstract void writeDouble(double dub) throws TException; + + public abstract void writeString(String str) throws TException; + + public abstract void writeBinary(ByteBuffer buf) throws TException; + + /** + * Reading methods. + */ + + public abstract TMessage readMessageBegin() throws TException; + + public abstract void readMessageEnd() throws TException; + + public abstract TStruct readStructBegin() throws TException; + + public abstract void readStructEnd() throws TException; + + public abstract TField readFieldBegin() throws TException; + + public abstract void readFieldEnd() throws TException; + + public abstract TMap readMapBegin() throws TException; + + public abstract void readMapEnd() throws TException; + + public abstract TList readListBegin() throws TException; + + public abstract void readListEnd() throws TException; + + public abstract TSet readSetBegin() throws TException; + + public abstract void readSetEnd() throws TException; + + public abstract boolean readBool() throws TException; + + public abstract byte readByte() throws TException; + + public abstract short readI16() throws TException; + + public abstract int readI32() throws TException; + + public abstract long readI64() throws TException; + + public abstract double readDouble() throws TException; + + public abstract String readString() throws TException; + + public abstract ByteBuffer readBinary() throws TException; + + /** + * Reset any internal state back to a blank slate. This method only needs to + * be implemented for stateful protocols. + */ + public void reset() {} + + /** + * Scheme accessor + */ + public Class<? extends IScheme> getScheme() { + return StandardScheme.class; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java new file mode 100644 index 000000000..2d29cd231 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolDecorator.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; + +import java.nio.ByteBuffer; + +/** + * <code>TProtocolDecorator</code> forwards all requests to an enclosed + * <code>TProtocol</code> instance, providing a way to author concise + * concrete decorator subclasses. While it has no abstract methods, it + * is marked abstract as a reminder that by itself, it does not modify + * the behaviour of the enclosed <code>TProtocol</code>. + * + * <p>See p.175 of Design Patterns (by Gamma et al.)</p> + * + * @see org.apache.thrift.protocol.TMultiplexedProtocol + */ +public abstract class TProtocolDecorator extends TProtocol { + + private final TProtocol concreteProtocol; + + /** + * Encloses the specified protocol. + * @param protocol All operations will be forward to this protocol. Must be non-null. + */ + public TProtocolDecorator(TProtocol protocol) { + super(protocol.getTransport()); + concreteProtocol = protocol; + } + + public void writeMessageBegin(TMessage tMessage) throws TException { + concreteProtocol.writeMessageBegin(tMessage); + } + + public void writeMessageEnd() throws TException { + concreteProtocol.writeMessageEnd(); + } + + public void writeStructBegin(TStruct tStruct) throws TException { + concreteProtocol.writeStructBegin(tStruct); + } + + public void writeStructEnd() throws TException { + concreteProtocol.writeStructEnd(); + } + + public void writeFieldBegin(TField tField) throws TException { + concreteProtocol.writeFieldBegin(tField); + } + + public void writeFieldEnd() throws TException { + concreteProtocol.writeFieldEnd(); + } + + public void writeFieldStop() throws TException { + concreteProtocol.writeFieldStop(); + } + + public void writeMapBegin(TMap tMap) throws TException { + concreteProtocol.writeMapBegin(tMap); + } + + public void writeMapEnd() throws TException { + concreteProtocol.writeMapEnd(); + } + + public void writeListBegin(TList tList) throws TException { + concreteProtocol.writeListBegin(tList); + } + + public void writeListEnd() throws TException { + concreteProtocol.writeListEnd(); + } + + public void writeSetBegin(TSet tSet) throws TException { + concreteProtocol.writeSetBegin(tSet); + } + + public void writeSetEnd() throws TException { + concreteProtocol.writeSetEnd(); + } + + public void writeBool(boolean b) throws TException { + concreteProtocol.writeBool(b); + } + + public void writeByte(byte b) throws TException { + concreteProtocol.writeByte(b); + } + + public void writeI16(short i) throws TException { + concreteProtocol.writeI16(i); + } + + public void writeI32(int i) throws TException { + concreteProtocol.writeI32(i); + } + + public void writeI64(long l) throws TException { + concreteProtocol.writeI64(l); + } + + public void writeDouble(double v) throws TException { + concreteProtocol.writeDouble(v); + } + + public void writeString(String s) throws TException { + concreteProtocol.writeString(s); + } + + public void writeBinary(ByteBuffer buf) throws TException { + concreteProtocol.writeBinary(buf); + } + + public TMessage readMessageBegin() throws TException { + return concreteProtocol.readMessageBegin(); + } + + public void readMessageEnd() throws TException { + concreteProtocol.readMessageEnd(); + } + + public TStruct readStructBegin() throws TException { + return concreteProtocol.readStructBegin(); + } + + public void readStructEnd() throws TException { + concreteProtocol.readStructEnd(); + } + + public TField readFieldBegin() throws TException { + return concreteProtocol.readFieldBegin(); + } + + public void readFieldEnd() throws TException { + concreteProtocol.readFieldEnd(); + } + + public TMap readMapBegin() throws TException { + return concreteProtocol.readMapBegin(); + } + + public void readMapEnd() throws TException { + concreteProtocol.readMapEnd(); + } + + public TList readListBegin() throws TException { + return concreteProtocol.readListBegin(); + } + + public void readListEnd() throws TException { + concreteProtocol.readListEnd(); + } + + public TSet readSetBegin() throws TException { + return concreteProtocol.readSetBegin(); + } + + public void readSetEnd() throws TException { + concreteProtocol.readSetEnd(); + } + + public boolean readBool() throws TException { + return concreteProtocol.readBool(); + } + + public byte readByte() throws TException { + return concreteProtocol.readByte(); + } + + public short readI16() throws TException { + return concreteProtocol.readI16(); + } + + public int readI32() throws TException { + return concreteProtocol.readI32(); + } + + public long readI64() throws TException { + return concreteProtocol.readI64(); + } + + public double readDouble() throws TException { + return concreteProtocol.readDouble(); + } + + public String readString() throws TException { + return concreteProtocol.readString(); + } + + public ByteBuffer readBinary() throws TException { + return concreteProtocol.readBinary(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java new file mode 100644 index 000000000..870f1b939 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolException.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; + +/** + * Protocol exceptions. + * + */ +public class TProtocolException extends TException { + + + private static final long serialVersionUID = 1L; + public static final int UNKNOWN = 0; + public static final int INVALID_DATA = 1; + public static final int NEGATIVE_SIZE = 2; + public static final int SIZE_LIMIT = 3; + public static final int BAD_VERSION = 4; + public static final int NOT_IMPLEMENTED = 5; + public static final int DEPTH_LIMIT = 6; + + protected int type_ = UNKNOWN; + + public TProtocolException() { + super(); + } + + public TProtocolException(int type) { + super(); + type_ = type; + } + + public TProtocolException(int type, String message) { + super(message); + type_ = type; + } + + public TProtocolException(String message) { + super(message); + } + + public TProtocolException(int type, Throwable cause) { + super(cause); + type_ = type; + } + + public TProtocolException(Throwable cause) { + super(cause); + } + + public TProtocolException(String message, Throwable cause) { + super(message, cause); + } + + public TProtocolException(int type, String message, Throwable cause) { + super(message, cause); + type_ = type; + } + + public int getType() { + return type_; + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java new file mode 100644 index 000000000..b72e87b38 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolFactory.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.io.Serializable; + +import org.apache.thrift.transport.TTransport; + +/** + * Factory interface for constructing protocol instances. + */ +public interface TProtocolFactory extends Serializable { + public TProtocol getProtocol(TTransport trans); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java new file mode 100644 index 000000000..cdaa30b87 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TProtocolUtil.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; + +/** + * Utility class with static methods for interacting with protocol data + * streams. + * + */ +public class TProtocolUtil { + + /** + * The maximum recursive depth the skip() function will traverse before + * throwing a TException. + */ + private static int maxSkipDepth = Integer.MAX_VALUE; + + /** + * Specifies the maximum recursive depth that the skip function will + * traverse before throwing a TException. This is a global setting, so + * any call to skip in this JVM will enforce this value. + * + * @param depth the maximum recursive depth. A value of 2 would allow + * the skip function to skip a structure or collection with basic children, + * but it would not permit skipping a struct that had a field containing + * a child struct. A value of 1 would only allow skipping of simple + * types and empty structs/collections. + */ + public static void setMaxSkipDepth(int depth) { + maxSkipDepth = depth; + } + + /** + * Skips over the next data element from the provided input TProtocol object. + * + * @param prot the protocol object to read from + * @param type the next value will be interpreted as this TType value. + */ + public static void skip(TProtocol prot, byte type) + throws TException { + skip(prot, type, maxSkipDepth); + } + + /** + * Skips over the next data element from the provided input TProtocol object. + * + * @param prot the protocol object to read from + * @param type the next value will be interpreted as this TType value. + * @param maxDepth this function will only skip complex objects to this + * recursive depth, to prevent Java stack overflow. + */ + public static void skip(TProtocol prot, byte type, int maxDepth) + throws TException { + if (maxDepth <= 0) { + throw new TException("Maximum skip depth exceeded"); + } + switch (type) { + case TType.BOOL: + prot.readBool(); + break; + + case TType.BYTE: + prot.readByte(); + break; + + case TType.I16: + prot.readI16(); + break; + + case TType.I32: + prot.readI32(); + break; + + case TType.I64: + prot.readI64(); + break; + + case TType.DOUBLE: + prot.readDouble(); + break; + + case TType.STRING: + prot.readBinary(); + break; + + case TType.STRUCT: + prot.readStructBegin(); + while (true) { + TField field = prot.readFieldBegin(); + if (field.type == TType.STOP) { + break; + } + skip(prot, field.type, maxDepth - 1); + prot.readFieldEnd(); + } + prot.readStructEnd(); + break; + + case TType.MAP: + TMap map = prot.readMapBegin(); + for (int i = 0; i < map.size; i++) { + skip(prot, map.keyType, maxDepth - 1); + skip(prot, map.valueType, maxDepth - 1); + } + prot.readMapEnd(); + break; + + case TType.SET: + TSet set = prot.readSetBegin(); + for (int i = 0; i < set.size; i++) { + skip(prot, set.elemType, maxDepth - 1); + } + prot.readSetEnd(); + break; + + case TType.LIST: + TList list = prot.readListBegin(); + for (int i = 0; i < list.size; i++) { + skip(prot, list.elemType, maxDepth - 1); + } + prot.readListEnd(); + break; + + default: + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Unrecognized type " + type); + } + } + + /** + * Attempt to determine the protocol used to serialize some data. + * + * The guess is based on known specificities of supported protocols. + * In some cases, no guess can be done, in that case we return the + * fallback TProtocolFactory. + * To be certain to correctly detect the protocol, the first encoded + * field should have a field id < 256 + * + * @param data The serialized data to guess the protocol for. + * @param fallback The TProtocol to return if no guess can be made. + * @return a Class implementing TProtocolFactory which can be used to create a deserializer. + */ + public static TProtocolFactory guessProtocolFactory(byte[] data, TProtocolFactory fallback) { + // + // If the first and last bytes are opening/closing curly braces we guess the protocol as + // being TJSONProtocol. + // It could not be a TCompactBinary encoding for a field of type 0xb (Map) + // with delta id 7 as the last byte for TCompactBinary is always 0. + // + + if ('{' == data[0] && '}' == data[data.length - 1]) { + return new TJSONProtocol.Factory(); + } + + // + // If the last byte is not 0, then it cannot be TCompactProtocol, it must be + // TBinaryProtocol. + // + + if (data[data.length - 1] != 0) { + return new TBinaryProtocol.Factory(); + } + + // + // A first byte of value > 16 indicates TCompactProtocol was used, and the first byte + // encodes a delta field id (id <= 15) and a field type. + // + + if (data[0] > 0x10) { + return new TCompactProtocol.Factory(); + } + + // + // If the second byte is 0 then it is a field id < 256 encoded by TBinaryProtocol. + // It cannot possibly be TCompactProtocol since a value of 0 would imply a field id + // of 0 as the zig zag varint encoding would end. + // + + if (data.length > 1 && 0 == data[1]) { + return new TBinaryProtocol.Factory(); + } + + // + // If bit 7 of the first byte of the field id is set then we have two choices: + // 1. A field id > 63 was encoded with TCompactProtocol. + // 2. A field id > 0x7fff (32767) was encoded with TBinaryProtocol and the last byte of the + // serialized data is 0. + // Option 2 is impossible since field ids are short and thus limited to 32767. + // + + if (data.length > 1 && (data[1] & 0x80) != 0) { + return new TCompactProtocol.Factory(); + } + + // + // The remaining case is either a field id <= 63 encoded as TCompactProtocol, + // one >= 256 encoded with TBinaryProtocol with a last byte at 0, or an empty structure. + // As we cannot really decide, we return the fallback protocol. + // + return fallback; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java new file mode 100644 index 000000000..38be9a991 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSet.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates set metadata. + * + */ +public final class TSet { + public TSet() { + this(TType.STOP, 0); + } + + public TSet(byte t, int s) { + elemType = t; + size = s; + } + + public TSet(TList list) { + this(list.elemType, list.size); + } + + public final byte elemType; + public final int size; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java new file mode 100644 index 000000000..eb7e23bf9 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TSimpleJSONProtocol.java @@ -0,0 +1,483 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Stack; + +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * JSON protocol implementation for thrift. + * + * This protocol is write-only and produces a simple output format + * suitable for parsing by scripting languages. It should not be + * confused with the full-featured TJSONProtocol. + * + */ +public class TSimpleJSONProtocol extends TProtocol { + + /** + * Factory + */ + public static class Factory implements TProtocolFactory { + public TProtocol getProtocol(TTransport trans) { + return new TSimpleJSONProtocol(trans); + } + } + + private static final byte[] COMMA = new byte[] {','}; + private static final byte[] COLON = new byte[] {':'}; + private static final byte[] LBRACE = new byte[] {'{'}; + private static final byte[] RBRACE = new byte[] {'}'}; + private static final byte[] LBRACKET = new byte[] {'['}; + private static final byte[] RBRACKET = new byte[] {']'}; + private static final char QUOTE = '"'; + + private static final TStruct ANONYMOUS_STRUCT = new TStruct(); + private static final TField ANONYMOUS_FIELD = new TField(); + private static final TMessage EMPTY_MESSAGE = new TMessage(); + private static final TSet EMPTY_SET = new TSet(); + private static final TList EMPTY_LIST = new TList(); + private static final TMap EMPTY_MAP = new TMap(); + private static final String LIST = "list"; + private static final String SET = "set"; + private static final String MAP = "map"; + + protected class Context { + protected void write() throws TException {} + + /** + * Returns whether the current value is a key in a map + */ + protected boolean isMapKey() { return false; } + } + + protected class ListContext extends Context { + protected boolean first_ = true; + + protected void write() throws TException { + if (first_) { + first_ = false; + } else { + trans_.write(COMMA); + } + } + } + + protected class StructContext extends Context { + protected boolean first_ = true; + protected boolean colon_ = true; + + protected void write() throws TException { + if (first_) { + first_ = false; + colon_ = true; + } else { + trans_.write(colon_ ? COLON : COMMA); + colon_ = !colon_; + } + } + } + + protected class MapContext extends StructContext { + protected boolean isKey = true; + + @Override + protected void write() throws TException { + super.write(); + isKey = !isKey; + } + + protected boolean isMapKey() { + // we want to coerce map keys to json strings regardless + // of their type + return isKey; + } + } + + protected final Context BASE_CONTEXT = new Context(); + + /** + * Stack of nested contexts that we may be in. + */ + protected Stack<Context> writeContextStack_ = new Stack<Context>(); + + /** + * Current context that we are in + */ + protected Context writeContext_ = BASE_CONTEXT; + + /** + * Push a new write context onto the stack. + */ + protected void pushWriteContext(Context c) { + writeContextStack_.push(writeContext_); + writeContext_ = c; + } + + /** + * Pop the last write context off the stack + */ + protected void popWriteContext() { + writeContext_ = writeContextStack_.pop(); + } + + /** + * Reset the write context stack to its initial state. + */ + protected void resetWriteContext() { + while (!writeContextStack_.isEmpty()) { + popWriteContext(); + } + } + + /** + * Used to make sure that we are not encountering a map whose keys are containers + */ + protected void assertContextIsNotMapKey(String invalidKeyType) throws CollectionMapKeyException { + if (writeContext_.isMapKey()) { + throw new CollectionMapKeyException("Cannot serialize a map with keys that are of type " + invalidKeyType); + } + } + + /** + * Constructor + */ + public TSimpleJSONProtocol(TTransport trans) { + super(trans); + } + + @Override + public void writeMessageBegin(TMessage message) throws TException { + resetWriteContext(); // THRIFT-3743 + trans_.write(LBRACKET); + pushWriteContext(new ListContext()); + writeString(message.name); + writeByte(message.type); + writeI32(message.seqid); + } + + @Override + public void writeMessageEnd() throws TException { + popWriteContext(); + trans_.write(RBRACKET); + } + + @Override + public void writeStructBegin(TStruct struct) throws TException { + writeContext_.write(); + trans_.write(LBRACE); + pushWriteContext(new StructContext()); + } + + @Override + public void writeStructEnd() throws TException { + popWriteContext(); + trans_.write(RBRACE); + } + + @Override + public void writeFieldBegin(TField field) throws TException { + // Note that extra type information is omitted in JSON! + writeString(field.name); + } + + @Override + public void writeFieldEnd() throws TException {} + + @Override + public void writeFieldStop() throws TException {} + + @Override + public void writeMapBegin(TMap map) throws TException { + assertContextIsNotMapKey(MAP); + writeContext_.write(); + trans_.write(LBRACE); + pushWriteContext(new MapContext()); + // No metadata! + } + + @Override + public void writeMapEnd() throws TException { + popWriteContext(); + trans_.write(RBRACE); + } + + @Override + public void writeListBegin(TList list) throws TException { + assertContextIsNotMapKey(LIST); + writeContext_.write(); + trans_.write(LBRACKET); + pushWriteContext(new ListContext()); + // No metadata! + } + + @Override + public void writeListEnd() throws TException { + popWriteContext(); + trans_.write(RBRACKET); + } + + @Override + public void writeSetBegin(TSet set) throws TException { + assertContextIsNotMapKey(SET); + writeContext_.write(); + trans_.write(LBRACKET); + pushWriteContext(new ListContext()); + // No metadata! + } + + @Override + public void writeSetEnd() throws TException { + popWriteContext(); + trans_.write(RBRACKET); + } + + @Override + public void writeBool(boolean b) throws TException { + writeByte(b ? (byte)1 : (byte)0); + } + + @Override + public void writeByte(byte b) throws TException { + writeI32(b); + } + + @Override + public void writeI16(short i16) throws TException { + writeI32(i16); + } + + @Override + public void writeI32(int i32) throws TException { + if(writeContext_.isMapKey()) { + writeString(Integer.toString(i32)); + } else { + writeContext_.write(); + _writeStringData(Integer.toString(i32)); + } + } + + public void _writeStringData(String s) throws TException { + byte[] b = s.getBytes(StandardCharsets.UTF_8); + trans_.write(b); + } + + @Override + public void writeI64(long i64) throws TException { + if(writeContext_.isMapKey()) { + writeString(Long.toString(i64)); + } else { + writeContext_.write(); + _writeStringData(Long.toString(i64)); + } + } + + @Override + public void writeDouble(double dub) throws TException { + if(writeContext_.isMapKey()) { + writeString(Double.toString(dub)); + } else { + writeContext_.write(); + _writeStringData(Double.toString(dub)); + } + } + + @Override + public void writeString(String str) throws TException { + writeContext_.write(); + int length = str.length(); + StringBuffer escape = new StringBuffer(length + 16); + escape.append(QUOTE); + for (int i = 0; i < length; ++i) { + char c = str.charAt(i); + switch (c) { + case '"': + case '\\': + escape.append('\\'); + escape.append(c); + break; + case '\b': + escape.append('\\'); + escape.append('b'); + break; + case '\f': + escape.append('\\'); + escape.append('f'); + break; + case '\n': + escape.append('\\'); + escape.append('n'); + break; + case '\r': + escape.append('\\'); + escape.append('r'); + break; + case '\t': + escape.append('\\'); + escape.append('t'); + break; + default: + // Control characters! According to JSON RFC u0020 (space) + if (c < ' ') { + String hex = Integer.toHexString(c); + escape.append('\\'); + escape.append('u'); + for (int j = 4; j > hex.length(); --j) { + escape.append('0'); + } + escape.append(hex); + } else { + escape.append(c); + } + break; + } + } + escape.append(QUOTE); + _writeStringData(escape.toString()); + } + + @Override + public void writeBinary(ByteBuffer bin) throws TException { + // TODO(mcslee): Fix this + writeString(new String(bin.array(), bin.position() + bin.arrayOffset(), + bin.limit() - bin.position() - bin.arrayOffset(), + StandardCharsets.UTF_8)); + } + + /** + * Reading methods. + */ + + @Override + public TMessage readMessageBegin() throws TException { + // TODO(mcslee): implement + return EMPTY_MESSAGE; + } + + @Override + public void readMessageEnd() throws TException {} + + @Override + public TStruct readStructBegin() throws TException { + // TODO(mcslee): implement + return ANONYMOUS_STRUCT; + } + + @Override + public void readStructEnd() throws TException {} + + @Override + public TField readFieldBegin() throws TException { + // TODO(mcslee): implement + return ANONYMOUS_FIELD; + } + + @Override + public void readFieldEnd() throws TException {} + + @Override + public TMap readMapBegin() throws TException { + // TODO(mcslee): implement + return EMPTY_MAP; + } + + @Override + public void readMapEnd() throws TException {} + + @Override + public TList readListBegin() throws TException { + // TODO(mcslee): implement + return EMPTY_LIST; + } + + @Override + public void readListEnd() throws TException {} + + @Override + public TSet readSetBegin() throws TException { + // TODO(mcslee): implement + return EMPTY_SET; + } + + @Override + public void readSetEnd() throws TException {} + + @Override + public boolean readBool() throws TException { + return (readByte() == 1); + } + + @Override + public byte readByte() throws TException { + // TODO(mcslee): implement + return 0; + } + + @Override + public short readI16() throws TException { + // TODO(mcslee): implement + return 0; + } + + @Override + public int readI32() throws TException { + // TODO(mcslee): implement + return 0; + } + + @Override + public long readI64() throws TException { + // TODO(mcslee): implement + return 0; + } + + @Override + public double readDouble() throws TException { + // TODO(mcslee): implement + return 0; + } + + @Override + public String readString() throws TException { + // TODO(mcslee): implement + return ""; + } + + public String readStringBody(int size) throws TException { + // TODO(mcslee): implement + return ""; + } + + @Override + public ByteBuffer readBinary() throws TException { + // TODO(mcslee): implement + return ByteBuffer.wrap(new byte[0]); + } + + public static class CollectionMapKeyException extends TException { + public CollectionMapKeyException(String message) { + super(message); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java new file mode 100644 index 000000000..a0f79012a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TStruct.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates struct metadata. + * + */ +public final class TStruct { + public TStruct() { + this(""); + } + + public TStruct(String n) { + name = n; + } + + public final String name; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java new file mode 100644 index 000000000..74f5226c8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.util.BitSet; + +import org.apache.thrift.TException; +import org.apache.thrift.scheme.IScheme; +import org.apache.thrift.scheme.TupleScheme; +import org.apache.thrift.transport.TTransport; + +public final class TTupleProtocol extends TCompactProtocol { + public static class Factory implements TProtocolFactory { + public Factory() {} + + public TProtocol getProtocol(TTransport trans) { + return new TTupleProtocol(trans); + } + } + + public TTupleProtocol(TTransport transport) { + super(transport); + } + + @Override + public Class<? extends IScheme> getScheme() { + return TupleScheme.class; + } + + public void writeBitSet(BitSet bs, int vectorWidth) throws TException { + byte[] bytes = toByteArray(bs, vectorWidth); + for (byte b : bytes) { + writeByte(b); + } + } + + public BitSet readBitSet(int i) throws TException { + int length = (int) Math.ceil(i/8.0); + byte[] bytes = new byte[length]; + for (int j = 0; j < length; j++) { + bytes[j] = readByte(); + } + BitSet bs = fromByteArray(bytes); + return bs; + } + + /** + * Returns a bitset containing the values in bytes. The byte-ordering must be + * big-endian. + */ + public static BitSet fromByteArray(byte[] bytes) { + BitSet bits = new BitSet(); + for (int i = 0; i < bytes.length * 8; i++) { + if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) { + bits.set(i); + } + } + return bits; + } + + /** + * Returns a byte array of at least length 1. The most significant bit in the + * result is guaranteed not to be a 1 (since BitSet does not support sign + * extension). The byte-ordering of the result is big-endian which means the + * most significant bit is in element 0. The bit at index 0 of the bit set is + * assumed to be the least significant bit. + * + * @param bits + * @param vectorWidth + * @return a byte array of at least length 1 + */ + public static byte[] toByteArray(BitSet bits, int vectorWidth) { + byte[] bytes = new byte[(int) Math.ceil(vectorWidth/8.0)]; + for (int i = 0; i < bits.length(); i++) { + if (bits.get(i)) { + bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8); + } + } + return bytes; + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java new file mode 100644 index 000000000..c3c1a0abd --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TType.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Type constants in the Thrift protocol. + */ +public final class TType { + public static final byte STOP = 0; + public static final byte VOID = 1; + public static final byte BOOL = 2; + public static final byte BYTE = 3; + public static final byte DOUBLE = 4; + public static final byte I16 = 6; + public static final byte I32 = 8; + public static final byte I64 = 10; + public static final byte STRING = 11; + public static final byte STRUCT = 12; + public static final byte MAP = 13; + public static final byte SET = 14; + public static final byte LIST = 15; + public static final byte ENUM = 16; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java new file mode 100644 index 000000000..aa3550705 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/IScheme.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.scheme; + +import org.apache.thrift.TBase; + +public interface IScheme<T extends TBase> { + + public void read(org.apache.thrift.protocol.TProtocol iproto, T struct) throws org.apache.thrift.TException; + + public void write(org.apache.thrift.protocol.TProtocol oproto, T struct) throws org.apache.thrift.TException; + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java new file mode 100644 index 000000000..006a66805 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/SchemeFactory.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.scheme; + +public interface SchemeFactory { + + public <S extends IScheme> S getScheme(); + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java new file mode 100644 index 000000000..ffab04db6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/StandardScheme.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.scheme; + +import org.apache.thrift.TBase; + +public abstract class StandardScheme<T extends TBase> implements IScheme<T> { + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java new file mode 100644 index 000000000..365242b11 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/scheme/TupleScheme.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.scheme; + +import org.apache.thrift.TBase; + +public abstract class TupleScheme<T extends TBase> implements IScheme<T> { + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java new file mode 100644 index 000000000..8c206e427 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java @@ -0,0 +1,618 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.TAsyncProcessor; +import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TIOStreamTransport; +import org.apache.thrift.transport.TMemoryInputTransport; +import org.apache.thrift.transport.TNonblockingServerTransport; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Provides common methods and classes used by nonblocking TServer + * implementations. + */ +public abstract class AbstractNonblockingServer extends TServer { + protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName()); + + public static abstract class AbstractNonblockingServerArgs<T extends AbstractNonblockingServerArgs<T>> extends AbstractServerArgs<T> { + public long maxReadBufferBytes = 256 * 1024 * 1024; + + public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) { + super(transport); + transportFactory(new TFramedTransport.Factory()); + } + } + + /** + * The maximum amount of memory we will allocate to client IO buffers at a + * time. Without this limit, the server will gladly allocate client buffers + * right into an out of memory exception, rather than waiting. + */ + final long MAX_READ_BUFFER_BYTES; + + /** + * How many bytes are currently allocated to read buffers. + */ + final AtomicLong readBufferBytesAllocated = new AtomicLong(0); + + public AbstractNonblockingServer(AbstractNonblockingServerArgs args) { + super(args); + MAX_READ_BUFFER_BYTES = args.maxReadBufferBytes; + } + + /** + * Begin accepting connections and processing invocations. + */ + public void serve() { + // start any IO threads + if (!startThreads()) { + return; + } + + // start listening, or exit + if (!startListening()) { + return; + } + + setServing(true); + + // this will block while we serve + waitForShutdown(); + + setServing(false); + + // do a little cleanup + stopListening(); + } + + /** + * Starts any threads required for serving. + * + * @return true if everything went ok, false if threads could not be started. + */ + protected abstract boolean startThreads(); + + /** + * A method that will block until when threads handling the serving have been + * shut down. + */ + protected abstract void waitForShutdown(); + + /** + * Have the server transport start accepting connections. + * + * @return true if we started listening successfully, false if something went + * wrong. + */ + protected boolean startListening() { + try { + serverTransport_.listen(); + return true; + } catch (TTransportException ttx) { + LOGGER.error("Failed to start listening on server socket!", ttx); + return false; + } + } + + /** + * Stop listening for connections. + */ + protected void stopListening() { + serverTransport_.close(); + } + + /** + * Perform an invocation. This method could behave several different ways - + * invoke immediately inline, queue for separate execution, etc. + * + * @return true if invocation was successfully requested, which is not a + * guarantee that invocation has completed. False if the request + * failed. + */ + protected abstract boolean requestInvoke(FrameBuffer frameBuffer); + + /** + * An abstract thread that handles selecting on a set of transports and + * {@link FrameBuffer FrameBuffers} associated with selected keys + * corresponding to requests. + */ + protected abstract class AbstractSelectThread extends Thread { + protected Selector selector; + + // List of FrameBuffers that want to change their selection interests. + protected final Set<FrameBuffer> selectInterestChanges = new HashSet<FrameBuffer>(); + + public AbstractSelectThread() throws IOException { + this.selector = SelectorProvider.provider().openSelector(); + } + + /** + * If the selector is blocked, wake it up. + */ + public void wakeupSelector() { + selector.wakeup(); + } + + /** + * Add FrameBuffer to the list of select interest changes and wake up the + * selector if it's blocked. When the select() call exits, it'll give the + * FrameBuffer a chance to change its interests. + */ + public void requestSelectInterestChange(FrameBuffer frameBuffer) { + synchronized (selectInterestChanges) { + selectInterestChanges.add(frameBuffer); + } + // wakeup the selector, if it's currently blocked. + selector.wakeup(); + } + + /** + * Check to see if there are any FrameBuffers that have switched their + * interest type from read to write or vice versa. + */ + protected void processInterestChanges() { + synchronized (selectInterestChanges) { + for (FrameBuffer fb : selectInterestChanges) { + fb.changeSelectInterests(); + } + selectInterestChanges.clear(); + } + } + + /** + * Do the work required to read from a readable client. If the frame is + * fully read, then invoke the method call. + */ + protected void handleRead(SelectionKey key) { + FrameBuffer buffer = (FrameBuffer) key.attachment(); + if (!buffer.read()) { + cleanupSelectionKey(key); + return; + } + + // if the buffer's frame read is complete, invoke the method. + if (buffer.isFrameFullyRead()) { + if (!requestInvoke(buffer)) { + cleanupSelectionKey(key); + } + } + } + + /** + * Let a writable client get written, if there's data to be written. + */ + protected void handleWrite(SelectionKey key) { + FrameBuffer buffer = (FrameBuffer) key.attachment(); + if (!buffer.write()) { + cleanupSelectionKey(key); + } + } + + /** + * Do connection-close cleanup on a given SelectionKey. + */ + protected void cleanupSelectionKey(SelectionKey key) { + // remove the records from the two maps + FrameBuffer buffer = (FrameBuffer) key.attachment(); + if (buffer != null) { + // close the buffer + buffer.close(); + } + // cancel the selection key + key.cancel(); + } + } // SelectThread + + /** + * Possible states for the FrameBuffer state machine. + */ + private enum FrameBufferState { + // in the midst of reading the frame size off the wire + READING_FRAME_SIZE, + // reading the actual frame data now, but not all the way done yet + READING_FRAME, + // completely read the frame, so an invocation can now happen + READ_FRAME_COMPLETE, + // waiting to get switched to listening for write events + AWAITING_REGISTER_WRITE, + // started writing response data, not fully complete yet + WRITING, + // another thread wants this framebuffer to go back to reading + AWAITING_REGISTER_READ, + // we want our transport and selection key invalidated in the selector + // thread + AWAITING_CLOSE + } + + /** + * Class that implements a sort of state machine around the interaction with a + * client and an invoker. It manages reading the frame size and frame data, + * getting it handed off as wrapped transports, and then the writing of + * response data back to the client. In the process it manages flipping the + * read and write bits on the selection key for its client. + */ + public class FrameBuffer { + private final Logger LOGGER = LoggerFactory.getLogger(getClass().getName()); + + // the actual transport hooked up to the client. + protected final TNonblockingTransport trans_; + + // the SelectionKey that corresponds to our transport + protected final SelectionKey selectionKey_; + + // the SelectThread that owns the registration of our transport + protected final AbstractSelectThread selectThread_; + + // where in the process of reading/writing are we? + protected FrameBufferState state_ = FrameBufferState.READING_FRAME_SIZE; + + // the ByteBuffer we'll be using to write and read, depending on the state + protected ByteBuffer buffer_; + + protected final TByteArrayOutputStream response_; + + // the frame that the TTransport should wrap. + protected final TMemoryInputTransport frameTrans_; + + // the transport that should be used to connect to clients + protected final TTransport inTrans_; + + protected final TTransport outTrans_; + + // the input protocol to use on frames + protected final TProtocol inProt_; + + // the output protocol to use on frames + protected final TProtocol outProt_; + + // context associated with this connection + protected final ServerContext context_; + + public FrameBuffer(final TNonblockingTransport trans, + final SelectionKey selectionKey, + final AbstractSelectThread selectThread) { + trans_ = trans; + selectionKey_ = selectionKey; + selectThread_ = selectThread; + buffer_ = ByteBuffer.allocate(4); + + frameTrans_ = new TMemoryInputTransport(); + response_ = new TByteArrayOutputStream(); + inTrans_ = inputTransportFactory_.getTransport(frameTrans_); + outTrans_ = outputTransportFactory_.getTransport(new TIOStreamTransport(response_)); + inProt_ = inputProtocolFactory_.getProtocol(inTrans_); + outProt_ = outputProtocolFactory_.getProtocol(outTrans_); + + if (eventHandler_ != null) { + context_ = eventHandler_.createContext(inProt_, outProt_); + } else { + context_ = null; + } + } + + /** + * Give this FrameBuffer a chance to read. The selector loop should have + * received a read event for this FrameBuffer. + * + * @return true if the connection should live on, false if it should be + * closed + */ + public boolean read() { + if (state_ == FrameBufferState.READING_FRAME_SIZE) { + // try to read the frame size completely + if (!internalRead()) { + return false; + } + + // if the frame size has been read completely, then prepare to read the + // actual frame. + if (buffer_.remaining() == 0) { + // pull out the frame size as an integer. + int frameSize = buffer_.getInt(0); + if (frameSize <= 0) { + LOGGER.error("Read an invalid frame size of " + frameSize + + ". Are you using TFramedTransport on the client side?"); + return false; + } + + // if this frame will always be too large for this server, log the + // error and close the connection. + if (frameSize > MAX_READ_BUFFER_BYTES) { + LOGGER.error("Read a frame size of " + frameSize + + ", which is bigger than the maximum allowable buffer size for ALL connections."); + return false; + } + + // if this frame will push us over the memory limit, then return. + // with luck, more memory will free up the next time around. + if (readBufferBytesAllocated.get() + frameSize > MAX_READ_BUFFER_BYTES) { + return true; + } + + // increment the amount of memory allocated to read buffers + readBufferBytesAllocated.addAndGet(frameSize + 4); + + // reallocate the readbuffer as a frame-sized buffer + buffer_ = ByteBuffer.allocate(frameSize + 4); + buffer_.putInt(frameSize); + + state_ = FrameBufferState.READING_FRAME; + } else { + // this skips the check of READING_FRAME state below, since we can't + // possibly go on to that state if there's data left to be read at + // this one. + return true; + } + } + + // it is possible to fall through from the READING_FRAME_SIZE section + // to READING_FRAME if there's already some frame data available once + // READING_FRAME_SIZE is complete. + + if (state_ == FrameBufferState.READING_FRAME) { + if (!internalRead()) { + return false; + } + + // since we're already in the select loop here for sure, we can just + // modify our selection key directly. + if (buffer_.remaining() == 0) { + // get rid of the read select interests + selectionKey_.interestOps(0); + state_ = FrameBufferState.READ_FRAME_COMPLETE; + } + + return true; + } + + // if we fall through to this point, then the state must be invalid. + LOGGER.error("Read was called but state is invalid (" + state_ + ")"); + return false; + } + + /** + * Give this FrameBuffer a chance to write its output to the final client. + */ + public boolean write() { + if (state_ == FrameBufferState.WRITING) { + try { + if (trans_.write(buffer_) < 0) { + return false; + } + } catch (IOException e) { + LOGGER.warn("Got an IOException during write!", e); + return false; + } + + // we're done writing. now we need to switch back to reading. + if (buffer_.remaining() == 0) { + prepareRead(); + } + return true; + } + + LOGGER.error("Write was called, but state is invalid (" + state_ + ")"); + return false; + } + + /** + * Give this FrameBuffer a chance to set its interest to write, once data + * has come in. + */ + public void changeSelectInterests() { + switch (state_) { + case AWAITING_REGISTER_WRITE: + // set the OP_WRITE interest + selectionKey_.interestOps(SelectionKey.OP_WRITE); + state_ = FrameBufferState.WRITING; + break; + case AWAITING_REGISTER_READ: + prepareRead(); + break; + case AWAITING_CLOSE: + close(); + selectionKey_.cancel(); + break; + default: + LOGGER.error( + "changeSelectInterest was called, but state is invalid ({})", + state_); + } + } + + /** + * Shut the connection down. + */ + public void close() { + // if we're being closed due to an error, we might have allocated a + // buffer that we need to subtract for our memory accounting. + if (state_ == FrameBufferState.READING_FRAME || + state_ == FrameBufferState.READ_FRAME_COMPLETE || + state_ == FrameBufferState.AWAITING_CLOSE) { + readBufferBytesAllocated.addAndGet(-buffer_.array().length); + } + trans_.close(); + if (eventHandler_ != null) { + eventHandler_.deleteContext(context_, inProt_, outProt_); + } + } + + /** + * Check if this FrameBuffer has a full frame read. + */ + public boolean isFrameFullyRead() { + return state_ == FrameBufferState.READ_FRAME_COMPLETE; + } + + /** + * After the processor has processed the invocation, whatever thread is + * managing invocations should call this method on this FrameBuffer so we + * know it's time to start trying to write again. Also, if it turns out that + * there actually isn't any data in the response buffer, we'll skip trying + * to write and instead go back to reading. + */ + public void responseReady() { + // the read buffer is definitely no longer in use, so we will decrement + // our read buffer count. we do this here as well as in close because + // we'd like to free this read memory up as quickly as possible for other + // clients. + readBufferBytesAllocated.addAndGet(-buffer_.array().length); + + if (response_.len() == 0) { + // go straight to reading again. this was probably an oneway method + state_ = FrameBufferState.AWAITING_REGISTER_READ; + buffer_ = null; + } else { + buffer_ = ByteBuffer.wrap(response_.get(), 0, response_.len()); + + // set state that we're waiting to be switched to write. we do this + // asynchronously through requestSelectInterestChange() because there is + // a possibility that we're not in the main thread, and thus currently + // blocked in select(). (this functionality is in place for the sake of + // the HsHa server.) + state_ = FrameBufferState.AWAITING_REGISTER_WRITE; + } + requestSelectInterestChange(); + } + + /** + * Actually invoke the method signified by this FrameBuffer. + */ + public void invoke() { + frameTrans_.reset(buffer_.array()); + response_.reset(); + + try { + if (eventHandler_ != null) { + eventHandler_.processContext(context_, inTrans_, outTrans_); + } + processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_); + responseReady(); + return; + } catch (TException te) { + LOGGER.warn("Exception while invoking!", te); + } catch (Throwable t) { + LOGGER.error("Unexpected throwable while invoking!", t); + } + // This will only be reached when there is a throwable. + state_ = FrameBufferState.AWAITING_CLOSE; + requestSelectInterestChange(); + } + + /** + * Perform a read into buffer. + * + * @return true if the read succeeded, false if there was an error or the + * connection closed. + */ + private boolean internalRead() { + try { + if (trans_.read(buffer_) < 0) { + return false; + } + return true; + } catch (IOException e) { + LOGGER.warn("Got an IOException in internalRead!", e); + return false; + } + } + + /** + * We're done writing, so reset our interest ops and change state + * accordingly. + */ + private void prepareRead() { + // we can set our interest directly without using the queue because + // we're in the select thread. + selectionKey_.interestOps(SelectionKey.OP_READ); + // get ready for another go-around + buffer_ = ByteBuffer.allocate(4); + state_ = FrameBufferState.READING_FRAME_SIZE; + } + + /** + * When this FrameBuffer needs to change its select interests and execution + * might not be in its select thread, then this method will make sure the + * interest change gets done when the select thread wakes back up. When the + * current thread is this FrameBuffer's select thread, then it just does the + * interest change immediately. + */ + protected void requestSelectInterestChange() { + if (Thread.currentThread() == this.selectThread_) { + changeSelectInterests(); + } else { + this.selectThread_.requestSelectInterestChange(this); + } + } + } // FrameBuffer + + public class AsyncFrameBuffer extends FrameBuffer { + public AsyncFrameBuffer(TNonblockingTransport trans, SelectionKey selectionKey, AbstractSelectThread selectThread) { + super(trans, selectionKey, selectThread); + } + + public TProtocol getInputProtocol() { + return inProt_; + } + + public TProtocol getOutputProtocol() { + return outProt_; + } + + + public void invoke() { + frameTrans_.reset(buffer_.array()); + response_.reset(); + + try { + if (eventHandler_ != null) { + eventHandler_.processContext(context_, inTrans_, outTrans_); + } + ((TAsyncProcessor)processorFactory_.getProcessor(inTrans_)).process(this); + return; + } catch (TException te) { + LOGGER.warn("Exception while invoking!", te); + } catch (Throwable t) { + LOGGER.error("Unexpected throwable while invoking!", t); + } + // This will only be reached when there is a throwable. + state_ = FrameBufferState.AWAITING_CLOSE; + requestSelectInterestChange(); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java new file mode 100644 index 000000000..e8210f419 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/Invocation.java @@ -0,0 +1,20 @@ +package org.apache.thrift.server; + +import org.apache.thrift.server.AbstractNonblockingServer.FrameBuffer; + +/** + * An Invocation represents a method call that is prepared to execute, given + * an idle worker thread. It contains the input and output protocols the + * thread's processor should use to perform the usual Thrift invocation. + */ +class Invocation implements Runnable { + private final FrameBuffer frameBuffer; + + public Invocation(final FrameBuffer frameBuffer) { + this.frameBuffer = frameBuffer; + } + + public void run() { + frameBuffer.invoke(); + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java new file mode 100644 index 000000000..9b0b99eea --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/ServerContext.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/** + * Interface for storing server's connection context + */ + +package org.apache.thrift.server; + +public interface ServerContext {} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java new file mode 100644 index 000000000..75082c0f7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TExtensibleServlet.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.thrift.TException; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; +import org.apache.thrift.transport.TTransport; + +/** + * Servlet implementation class ThriftServer, that allows {@link TProcessor} and + * {@link TProtocolFactory} to be supplied after the {@link #init()} method has + * finished. <br> + * Subclasses must implement the abstract methods that return the TProcessor and + * two TProtocolFactory. Those methods are guaranteed to be called exactly once, + * and that {@link ServletContext} is available. + */ +public abstract class TExtensibleServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + private TProcessor processor; + + private TProtocolFactory inFactory; + + private TProtocolFactory outFactory; + + private Collection<Map.Entry<String, String>> customHeaders; + + /** + * Returns the appropriate {@link TProcessor}. This will be called <b>once</b> just + * after the {@link #init()} method + * + * @return the appropriate {@link TProcessor} + */ + protected abstract TProcessor getProcessor(); + + /** + * Returns the appropriate in {@link TProtocolFactory}. This will be called + * <b>once</b> just after the {@link #init()} method + * + * @return the appropriate in {@link TProtocolFactory} + */ + protected abstract TProtocolFactory getInProtocolFactory(); + + /** + * Returns the appropriate out {@link TProtocolFactory}. This will be called + * <b>once</b> just after the {@link #init()} method + * + * @return the appropriate out {@link TProtocolFactory} + */ + protected abstract TProtocolFactory getOutProtocolFactory(); + + @Override + public final void init(ServletConfig config) throws ServletException { + super.init(config); //no-args init() happens here + this.processor = getProcessor(); + this.inFactory = getInProtocolFactory(); + this.outFactory = getOutProtocolFactory(); + this.customHeaders = new ArrayList<Map.Entry<String, String>>(); + + if (processor == null) { + throw new ServletException("processor must be set"); + } + if (inFactory == null) { + throw new ServletException("inFactory must be set"); + } + if (outFactory == null) { + throw new ServletException("outFactory must be set"); + } + } + + /** + * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse + * response) + */ + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + TTransport inTransport = null; + TTransport outTransport = null; + + try { + response.setContentType("application/x-thrift"); + + if (null != this.customHeaders) { + for (Map.Entry<String, String> header : this.customHeaders) { + response.addHeader(header.getKey(), header.getValue()); + } + } + + InputStream in = request.getInputStream(); + OutputStream out = response.getOutputStream(); + + TTransport transport = new TIOStreamTransport(in, out); + inTransport = transport; + outTransport = transport; + + TProtocol inProtocol = inFactory.getProtocol(inTransport); + TProtocol outProtocol = inFactory.getProtocol(outTransport); + + processor.process(inProtocol, outProtocol); + out.flush(); + } catch (TException te) { + throw new ServletException(te); + } + } + + /** + * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse + * response) + */ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + doPost(req, resp); + } + + public void addCustomHeader(final String key, final String value) { + this.customHeaders.add(new Map.Entry<String, String>() { + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public String setValue(String value) { + return null; + } + }); + } + + public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) { + this.customHeaders.clear(); + this.customHeaders.addAll(headers); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java new file mode 100644 index 000000000..4c5d7b5b5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/THsHaServer.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.thrift.transport.TNonblockingServerTransport; + +/** + * An extension of the TNonblockingServer to a Half-Sync/Half-Async server. + * Like TNonblockingServer, it relies on the use of TFramedTransport. + */ +public class THsHaServer extends TNonblockingServer { + + public static class Args extends AbstractNonblockingServerArgs<Args> { + public int minWorkerThreads = 5; + public int maxWorkerThreads = Integer.MAX_VALUE; + private int stopTimeoutVal = 60; + private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS; + private ExecutorService executorService = null; + + public Args(TNonblockingServerTransport transport) { + super(transport); + } + + + /** + * Sets the min and max threads. + * + * @deprecated use {@link #minWorkerThreads(int)} and {@link #maxWorkerThreads(int)} instead. + */ + @Deprecated + public Args workerThreads(int n) { + minWorkerThreads = n; + maxWorkerThreads = n; + return this; + } + + /** + * @return what the min threads was set to. + * @deprecated use {@link #getMinWorkerThreads()} and {@link #getMaxWorkerThreads()} instead. + */ + @Deprecated + public int getWorkerThreads() { + return minWorkerThreads; + } + + public Args minWorkerThreads(int n) { + minWorkerThreads = n; + return this; + } + + public Args maxWorkerThreads(int n) { + maxWorkerThreads = n; + return this; + } + + public int getMinWorkerThreads() { + return minWorkerThreads; + } + + public int getMaxWorkerThreads() { + return maxWorkerThreads; + } + + public int getStopTimeoutVal() { + return stopTimeoutVal; + } + + public Args stopTimeoutVal(int stopTimeoutVal) { + this.stopTimeoutVal = stopTimeoutVal; + return this; + } + + public TimeUnit getStopTimeoutUnit() { + return stopTimeoutUnit; + } + + public Args stopTimeoutUnit(TimeUnit stopTimeoutUnit) { + this.stopTimeoutUnit = stopTimeoutUnit; + return this; + } + + public ExecutorService getExecutorService() { + return executorService; + } + + public Args executorService(ExecutorService executorService) { + this.executorService = executorService; + return this; + } + } + + + // This wraps all the functionality of queueing and thread pool management + // for the passing of Invocations from the Selector to workers. + private final ExecutorService invoker; + + private final Args args; + + /** + * Create the server with the specified Args configuration + */ + public THsHaServer(Args args) { + super(args); + + invoker = args.executorService == null ? createInvokerPool(args) : args.executorService; + this.args = args; + } + + /** + * {@inheritDoc} + */ + @Override + protected void waitForShutdown() { + joinSelector(); + gracefullyShutdownInvokerPool(); + } + + /** + * Helper to create an invoker pool + */ + protected static ExecutorService createInvokerPool(Args options) { + int minWorkerThreads = options.minWorkerThreads; + int maxWorkerThreads = options.maxWorkerThreads; + int stopTimeoutVal = options.stopTimeoutVal; + TimeUnit stopTimeoutUnit = options.stopTimeoutUnit; + + LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); + ExecutorService invoker = new ThreadPoolExecutor(minWorkerThreads, + maxWorkerThreads, stopTimeoutVal, stopTimeoutUnit, queue); + + return invoker; + } + + protected ExecutorService getInvoker() { + return invoker; + } + + protected void gracefullyShutdownInvokerPool() { + // try to gracefully shut down the executor service + invoker.shutdown(); + + // Loop until awaitTermination finally does return without a interrupted + // exception. If we don't do this, then we'll shut down prematurely. We want + // to let the executorService clear it's task queue, closing client sockets + // appropriately. + long timeoutMS = args.stopTimeoutUnit.toMillis(args.stopTimeoutVal); + long now = System.currentTimeMillis(); + while (timeoutMS >= 0) { + try { + invoker.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS); + break; + } catch (InterruptedException ix) { + long newnow = System.currentTimeMillis(); + timeoutMS -= (newnow - now); + now = newnow; + } + } + } + + /** + * We override the standard invoke method here to queue the invocation for + * invoker service instead of immediately invoking. The thread pool takes care + * of the rest. + */ + @Override + protected boolean requestInvoke(FrameBuffer frameBuffer) { + try { + Runnable invocation = getRunnable(frameBuffer); + invoker.execute(invocation); + return true; + } catch (RejectedExecutionException rx) { + LOGGER.warn("ExecutorService rejected execution!", rx); + return false; + } + } + + protected Runnable getRunnable(FrameBuffer frameBuffer){ + return new Invocation(frameBuffer); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java new file mode 100644 index 000000000..79610b0f7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TNonblockingServer.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.transport.TNonblockingServerTransport; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransportException; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.util.Iterator; + +/** + * A nonblocking TServer implementation. This allows for fairness amongst all + * connected clients in terms of invocations. + * + * This server is inherently single-threaded. If you want a limited thread pool + * coupled with invocation-fairness, see THsHaServer. + * + * To use this server, you MUST use a TFramedTransport at the outermost + * transport, otherwise this server will be unable to determine when a whole + * method call has been read off the wire. Clients must also use TFramedTransport. + */ +public class TNonblockingServer extends AbstractNonblockingServer { + + public static class Args extends AbstractNonblockingServerArgs<Args> { + public Args(TNonblockingServerTransport transport) { + super(transport); + } + } + + private SelectAcceptThread selectAcceptThread_; + + public TNonblockingServer(AbstractNonblockingServerArgs args) { + super(args); + } + + + /** + * Start the selector thread to deal with accepts and client messages. + * + * @return true if everything went ok, false if we couldn't start for some + * reason. + */ + @Override + protected boolean startThreads() { + // start the selector + try { + selectAcceptThread_ = new SelectAcceptThread((TNonblockingServerTransport)serverTransport_); + selectAcceptThread_.start(); + return true; + } catch (IOException e) { + LOGGER.error("Failed to start selector thread!", e); + return false; + } + } + + @Override + protected void waitForShutdown() { + joinSelector(); + } + + /** + * Block until the selector thread exits. + */ + protected void joinSelector() { + // wait until the selector thread exits + try { + selectAcceptThread_.join(); + } catch (InterruptedException e) { + LOGGER.debug("Interrupted while waiting for accept thread", e); + Thread.currentThread().interrupt(); + } + } + + /** + * Stop serving and shut everything down. + */ + @Override + public void stop() { + stopped_ = true; + if (selectAcceptThread_ != null) { + selectAcceptThread_.wakeupSelector(); + } + } + + /** + * Perform an invocation. This method could behave several different ways + * - invoke immediately inline, queue for separate execution, etc. + */ + @Override + protected boolean requestInvoke(FrameBuffer frameBuffer) { + frameBuffer.invoke(); + return true; + } + + + public boolean isStopped() { + return selectAcceptThread_.isStopped(); + } + + /** + * The thread that will be doing all the selecting, managing new connections + * and those that still need to be read. + */ + protected class SelectAcceptThread extends AbstractSelectThread { + + // The server transport on which new client transports will be accepted + private final TNonblockingServerTransport serverTransport; + + /** + * Set up the thread that will handle the non-blocking accepts, reads, and + * writes. + */ + public SelectAcceptThread(final TNonblockingServerTransport serverTransport) + throws IOException { + this.serverTransport = serverTransport; + serverTransport.registerSelector(selector); + } + + public boolean isStopped() { + return stopped_; + } + + /** + * The work loop. Handles both selecting (all IO operations) and managing + * the selection preferences of all existing connections. + */ + public void run() { + try { + if (eventHandler_ != null) { + eventHandler_.preServe(); + } + + while (!stopped_) { + select(); + processInterestChanges(); + } + for (SelectionKey selectionKey : selector.keys()) { + cleanupSelectionKey(selectionKey); + } + } catch (Throwable t) { + LOGGER.error("run() exiting due to uncaught error", t); + } finally { + try { + selector.close(); + } catch (IOException e) { + LOGGER.error("Got an IOException while closing selector!", e); + } + stopped_ = true; + } + } + + /** + * Select and process IO events appropriately: + * If there are connections to be accepted, accept them. + * If there are existing connections with data waiting to be read, read it, + * buffering until a whole frame has been read. + * If there are any pending responses, buffer them until their target client + * is available, and then send the data. + */ + private void select() { + try { + // wait for io events. + selector.select(); + + // process the io events we received + Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator(); + while (!stopped_ && selectedKeys.hasNext()) { + SelectionKey key = selectedKeys.next(); + selectedKeys.remove(); + + // skip if not valid + if (!key.isValid()) { + cleanupSelectionKey(key); + continue; + } + + // if the key is marked Accept, then it has to be the server + // transport. + if (key.isAcceptable()) { + handleAccept(); + } else if (key.isReadable()) { + // deal with reads + handleRead(key); + } else if (key.isWritable()) { + // deal with writes + handleWrite(key); + } else { + LOGGER.warn("Unexpected state in select! " + key.interestOps()); + } + } + } catch (IOException e) { + LOGGER.warn("Got an IOException while selecting!", e); + } + } + + protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans, + final SelectionKey selectionKey, + final AbstractSelectThread selectThread) { + return processorFactory_.isAsyncProcessor() ? + new AsyncFrameBuffer(trans, selectionKey, selectThread) : + new FrameBuffer(trans, selectionKey, selectThread); + } + + /** + * Accept a new connection. + */ + private void handleAccept() throws IOException { + SelectionKey clientKey = null; + TNonblockingTransport client = null; + try { + // accept the connection + client = (TNonblockingTransport)serverTransport.accept(); + clientKey = client.registerSelector(selector, SelectionKey.OP_READ); + + // add this key to the map + FrameBuffer frameBuffer = createFrameBuffer(client, clientKey, SelectAcceptThread.this); + + clientKey.attach(frameBuffer); + } catch (TTransportException tte) { + // something went wrong accepting. + LOGGER.warn("Exception trying to accept!", tte); + if (clientKey != null) cleanupSelectionKey(clientKey); + if (client != null) client.close(); + } + } + } // SelectAcceptThread +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java new file mode 100644 index 000000000..bac06b26b --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServer.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.TProcessorFactory; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TServerTransport; +import org.apache.thrift.transport.TTransportFactory; + +/** + * Generic interface for a Thrift server. + * + */ +public abstract class TServer { + + public static class Args extends AbstractServerArgs<Args> { + public Args(TServerTransport transport) { + super(transport); + } + } + + public static abstract class AbstractServerArgs<T extends AbstractServerArgs<T>> { + final TServerTransport serverTransport; + TProcessorFactory processorFactory; + TTransportFactory inputTransportFactory = new TTransportFactory(); + TTransportFactory outputTransportFactory = new TTransportFactory(); + TProtocolFactory inputProtocolFactory = new TBinaryProtocol.Factory(); + TProtocolFactory outputProtocolFactory = new TBinaryProtocol.Factory(); + + public AbstractServerArgs(TServerTransport transport) { + serverTransport = transport; + } + + public T processorFactory(TProcessorFactory factory) { + this.processorFactory = factory; + return (T) this; + } + + public T processor(TProcessor processor) { + this.processorFactory = new TProcessorFactory(processor); + return (T) this; + } + + public T transportFactory(TTransportFactory factory) { + this.inputTransportFactory = factory; + this.outputTransportFactory = factory; + return (T) this; + } + + public T inputTransportFactory(TTransportFactory factory) { + this.inputTransportFactory = factory; + return (T) this; + } + + public T outputTransportFactory(TTransportFactory factory) { + this.outputTransportFactory = factory; + return (T) this; + } + + public T protocolFactory(TProtocolFactory factory) { + this.inputProtocolFactory = factory; + this.outputProtocolFactory = factory; + return (T) this; + } + + public T inputProtocolFactory(TProtocolFactory factory) { + this.inputProtocolFactory = factory; + return (T) this; + } + + public T outputProtocolFactory(TProtocolFactory factory) { + this.outputProtocolFactory = factory; + return (T) this; + } + } + + /** + * Core processor + */ + protected TProcessorFactory processorFactory_; + + /** + * Server transport + */ + protected TServerTransport serverTransport_; + + /** + * Input Transport Factory + */ + protected TTransportFactory inputTransportFactory_; + + /** + * Output Transport Factory + */ + protected TTransportFactory outputTransportFactory_; + + /** + * Input Protocol Factory + */ + protected TProtocolFactory inputProtocolFactory_; + + /** + * Output Protocol Factory + */ + protected TProtocolFactory outputProtocolFactory_; + + private volatile boolean isServing; + + protected TServerEventHandler eventHandler_; + + // Flag for stopping the server + // Please see THRIFT-1795 for the usage of this flag + protected volatile boolean stopped_ = false; + + protected TServer(AbstractServerArgs args) { + processorFactory_ = args.processorFactory; + serverTransport_ = args.serverTransport; + inputTransportFactory_ = args.inputTransportFactory; + outputTransportFactory_ = args.outputTransportFactory; + inputProtocolFactory_ = args.inputProtocolFactory; + outputProtocolFactory_ = args.outputProtocolFactory; + } + + /** + * The run method fires up the server and gets things going. + */ + public abstract void serve(); + + /** + * Stop the server. This is optional on a per-implementation basis. Not + * all servers are required to be cleanly stoppable. + */ + public void stop() {} + + public boolean isServing() { + return isServing; + } + + protected void setServing(boolean serving) { + isServing = serving; + } + + public void setServerEventHandler(TServerEventHandler eventHandler) { + eventHandler_ = eventHandler; + } + + public TServerEventHandler getEventHandler() { + return eventHandler_; + } + + public boolean getShouldStop() { + return this.stopped_; + } + + public void setShouldStop(boolean shouldStop) { + this.stopped_ = shouldStop; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java new file mode 100644 index 000000000..f069b9bfb --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServerEventHandler.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TTransport; + +/** + * Interface that can handle events from the server core. To + * use this you should subclass it and implement the methods that you care + * about. Your subclass can also store local data that you may care about, + * such as additional "arguments" to these methods (stored in the object + * instance's state). + */ +public interface TServerEventHandler { + + /** + * Called before the server begins. + */ + void preServe(); + + /** + * Called when a new client has connected and is about to being processing. + */ + ServerContext createContext(TProtocol input, + TProtocol output); + + /** + * Called when a client has finished request-handling to delete server + * context. + */ + void deleteContext(ServerContext serverContext, + TProtocol input, + TProtocol output); + + /** + * Called when a client is about to call the processor. + */ + void processContext(ServerContext serverContext, + TTransport inputTransport, TTransport outputTransport); + +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java new file mode 100644 index 000000000..c1ab9df55 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TServlet.java @@ -0,0 +1,119 @@ +package org.apache.thrift.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.thrift.TException; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; +import org.apache.thrift.transport.TTransport; + +/** + * Servlet implementation class ThriftServer + */ +public class TServlet extends HttpServlet { + + private final TProcessor processor; + + private final TProtocolFactory inProtocolFactory; + + private final TProtocolFactory outProtocolFactory; + + private final Collection<Map.Entry<String, String>> customHeaders; + + /** + * @see HttpServlet#HttpServlet() + */ + public TServlet(TProcessor processor, TProtocolFactory inProtocolFactory, + TProtocolFactory outProtocolFactory) { + super(); + this.processor = processor; + this.inProtocolFactory = inProtocolFactory; + this.outProtocolFactory = outProtocolFactory; + this.customHeaders = new ArrayList<Map.Entry<String, String>>(); + } + + /** + * @see HttpServlet#HttpServlet() + */ + public TServlet(TProcessor processor, TProtocolFactory protocolFactory) { + this(processor, protocolFactory, protocolFactory); + } + + /** + * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse + * response) + */ + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + TTransport inTransport = null; + TTransport outTransport = null; + + try { + response.setContentType("application/x-thrift"); + + if (null != this.customHeaders) { + for (Map.Entry<String, String> header : this.customHeaders) { + response.addHeader(header.getKey(), header.getValue()); + } + } + InputStream in = request.getInputStream(); + OutputStream out = response.getOutputStream(); + + TTransport transport = new TIOStreamTransport(in, out); + inTransport = transport; + outTransport = transport; + + TProtocol inProtocol = inProtocolFactory.getProtocol(inTransport); + TProtocol outProtocol = outProtocolFactory.getProtocol(outTransport); + + processor.process(inProtocol, outProtocol); + out.flush(); + } catch (TException te) { + throw new ServletException(te); + } + } + + /** + * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse + * response) + */ + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + doPost(request, response); + } + + public void addCustomHeader(final String key, final String value) { + this.customHeaders.add(new Map.Entry<String, String>() { + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public String setValue(String value) { + return null; + } + }); + } + + public void setCustomHeaders(Collection<Map.Entry<String, String>> headers) { + this.customHeaders.clear(); + this.customHeaders.addAll(headers); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java new file mode 100644 index 000000000..13501efc6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TSimpleServer.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.TException; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Simple singlethreaded server for testing. + * + */ +public class TSimpleServer extends TServer { + + private static final Logger LOGGER = LoggerFactory.getLogger(TSimpleServer.class.getName()); + + public TSimpleServer(AbstractServerArgs args) { + super(args); + } + + public void serve() { + try { + serverTransport_.listen(); + } catch (TTransportException ttx) { + LOGGER.error("Error occurred during listening.", ttx); + return; + } + + // Run the preServe event + if (eventHandler_ != null) { + eventHandler_.preServe(); + } + + setServing(true); + + while (!stopped_) { + TTransport client = null; + TProcessor processor = null; + TTransport inputTransport = null; + TTransport outputTransport = null; + TProtocol inputProtocol = null; + TProtocol outputProtocol = null; + ServerContext connectionContext = null; + try { + client = serverTransport_.accept(); + if (client != null) { + processor = processorFactory_.getProcessor(client); + inputTransport = inputTransportFactory_.getTransport(client); + outputTransport = outputTransportFactory_.getTransport(client); + inputProtocol = inputProtocolFactory_.getProtocol(inputTransport); + outputProtocol = outputProtocolFactory_.getProtocol(outputTransport); + if (eventHandler_ != null) { + connectionContext = eventHandler_.createContext(inputProtocol, outputProtocol); + } + while (true) { + if (eventHandler_ != null) { + eventHandler_.processContext(connectionContext, inputTransport, outputTransport); + } + processor.process(inputProtocol, outputProtocol); + } + } + } catch (TTransportException ttx) { + // Client died, just move on + } catch (TException tx) { + if (!stopped_) { + LOGGER.error("Thrift error occurred during processing of message.", tx); + } + } catch (Exception x) { + if (!stopped_) { + LOGGER.error("Error occurred during processing of message.", x); + } + } + + if (eventHandler_ != null) { + eventHandler_.deleteContext(connectionContext, inputProtocol, outputProtocol); + } + + if (inputTransport != null) { + inputTransport.close(); + } + + if (outputTransport != null) { + outputTransport.close(); + } + + } + setServing(false); + } + + public void stop() { + stopped_ = true; + serverTransport_.interrupt(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java new file mode 100644 index 000000000..87e873381 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadPoolServer.java @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.apache.thrift.TException; +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TServerTransport; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Server which uses Java's built in ThreadPool management to spawn off + * a worker pool that + * + */ +public class TThreadPoolServer extends TServer { + private static final Logger LOGGER = LoggerFactory.getLogger(TThreadPoolServer.class.getName()); + + public static class Args extends AbstractServerArgs<Args> { + public int minWorkerThreads = 5; + public int maxWorkerThreads = Integer.MAX_VALUE; + public ExecutorService executorService; + public int stopTimeoutVal = 60; + public TimeUnit stopTimeoutUnit = TimeUnit.SECONDS; + public int requestTimeout = 20; + public TimeUnit requestTimeoutUnit = TimeUnit.SECONDS; + public int beBackoffSlotLength = 100; + public TimeUnit beBackoffSlotLengthUnit = TimeUnit.MILLISECONDS; + + public Args(TServerTransport transport) { + super(transport); + } + + public Args minWorkerThreads(int n) { + minWorkerThreads = n; + return this; + } + + public Args maxWorkerThreads(int n) { + maxWorkerThreads = n; + return this; + } + + public Args stopTimeoutVal(int n) { + stopTimeoutVal = n; + return this; + } + + public Args stopTimeoutUnit(TimeUnit tu) { + stopTimeoutUnit = tu; + return this; + } + + public Args requestTimeout(int n) { + requestTimeout = n; + return this; + } + + public Args requestTimeoutUnit(TimeUnit tu) { + requestTimeoutUnit = tu; + return this; + } + //Binary exponential backoff slot length + public Args beBackoffSlotLength(int n) { + beBackoffSlotLength = n; + return this; + } + + //Binary exponential backoff slot time unit + public Args beBackoffSlotLengthUnit(TimeUnit tu) { + beBackoffSlotLengthUnit = tu; + return this; + } + + public Args executorService(ExecutorService executorService) { + this.executorService = executorService; + return this; + } + } + + // Executor service for handling client connections + private ExecutorService executorService_; + + private final TimeUnit stopTimeoutUnit; + + private final long stopTimeoutVal; + + private final TimeUnit requestTimeoutUnit; + + private final long requestTimeout; + + private final long beBackoffSlotInMillis; + + private Random random = new Random(System.currentTimeMillis()); + + public TThreadPoolServer(Args args) { + super(args); + + stopTimeoutUnit = args.stopTimeoutUnit; + stopTimeoutVal = args.stopTimeoutVal; + requestTimeoutUnit = args.requestTimeoutUnit; + requestTimeout = args.requestTimeout; + beBackoffSlotInMillis = args.beBackoffSlotLengthUnit.toMillis(args.beBackoffSlotLength); + + executorService_ = args.executorService != null ? + args.executorService : createDefaultExecutorService(args); + } + + private static ExecutorService createDefaultExecutorService(Args args) { + SynchronousQueue<Runnable> executorQueue = + new SynchronousQueue<Runnable>(); + return new ThreadPoolExecutor(args.minWorkerThreads, + args.maxWorkerThreads, + args.stopTimeoutVal, + args.stopTimeoutUnit, + executorQueue); + } + + protected ExecutorService getExecutorService() { + return executorService_; + } + + protected boolean preServe() { + try { + serverTransport_.listen(); + } catch (TTransportException ttx) { + LOGGER.error("Error occurred during listening.", ttx); + return false; + } + + // Run the preServe event + if (eventHandler_ != null) { + eventHandler_.preServe(); + } + stopped_ = false; + setServing(true); + + return true; + } + + public void serve() { + if (!preServe()) { + return; + } + + execute(); + waitForShutdown(); + + setServing(false); + } + + protected void execute() { + int failureCount = 0; + while (!stopped_) { + try { + TTransport client = serverTransport_.accept(); + WorkerProcess wp = new WorkerProcess(client); + + int retryCount = 0; + long remainTimeInMillis = requestTimeoutUnit.toMillis(requestTimeout); + while(true) { + try { + executorService_.execute(wp); + break; + } catch(Throwable t) { + if (t instanceof RejectedExecutionException) { + retryCount++; + try { + if (remainTimeInMillis > 0) { + //do a truncated 20 binary exponential backoff sleep + long sleepTimeInMillis = ((long) (random.nextDouble() * + (1L << Math.min(retryCount, 20)))) * beBackoffSlotInMillis; + sleepTimeInMillis = Math.min(sleepTimeInMillis, remainTimeInMillis); + TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis); + remainTimeInMillis = remainTimeInMillis - sleepTimeInMillis; + } else { + client.close(); + wp = null; + LOGGER.warn("Task has been rejected by ExecutorService " + retryCount + + " times till timedout, reason: " + t); + break; + } + } catch (InterruptedException e) { + LOGGER.warn("Interrupted while waiting to place client on executor queue."); + Thread.currentThread().interrupt(); + break; + } + } else if (t instanceof Error) { + LOGGER.error("ExecutorService threw error: " + t, t); + throw (Error)t; + } else { + //for other possible runtime errors from ExecutorService, should also not kill serve + LOGGER.warn("ExecutorService threw error: " + t, t); + break; + } + } + } + } catch (TTransportException ttx) { + if (!stopped_) { + ++failureCount; + LOGGER.warn("Transport error occurred during acceptance of message.", ttx); + } + } + } + } + + protected void waitForShutdown() { + executorService_.shutdown(); + + // Loop until awaitTermination finally does return without a interrupted + // exception. If we don't do this, then we'll shut down prematurely. We want + // to let the executorService clear it's task queue, closing client sockets + // appropriately. + long timeoutMS = stopTimeoutUnit.toMillis(stopTimeoutVal); + long now = System.currentTimeMillis(); + while (timeoutMS >= 0) { + try { + executorService_.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS); + break; + } catch (InterruptedException ix) { + long newnow = System.currentTimeMillis(); + timeoutMS -= (newnow - now); + now = newnow; + } + } + } + + public void stop() { + stopped_ = true; + serverTransport_.interrupt(); + } + + private class WorkerProcess implements Runnable { + + /** + * Client that this services. + */ + private TTransport client_; + + /** + * Default constructor. + * + * @param client Transport to process + */ + private WorkerProcess(TTransport client) { + client_ = client; + } + + /** + * Loops on processing a client forever + */ + public void run() { + TProcessor processor = null; + TTransport inputTransport = null; + TTransport outputTransport = null; + TProtocol inputProtocol = null; + TProtocol outputProtocol = null; + + TServerEventHandler eventHandler = null; + ServerContext connectionContext = null; + + try { + processor = processorFactory_.getProcessor(client_); + inputTransport = inputTransportFactory_.getTransport(client_); + outputTransport = outputTransportFactory_.getTransport(client_); + inputProtocol = inputProtocolFactory_.getProtocol(inputTransport); + outputProtocol = outputProtocolFactory_.getProtocol(outputTransport); + + eventHandler = getEventHandler(); + if (eventHandler != null) { + connectionContext = eventHandler.createContext(inputProtocol, outputProtocol); + } + // we check stopped_ first to make sure we're not supposed to be shutting + // down. this is necessary for graceful shutdown. + while (true) { + + if (eventHandler != null) { + eventHandler.processContext(connectionContext, inputTransport, outputTransport); + } + + if (stopped_) { + break; + } + processor.process(inputProtocol, outputProtocol); + } + } catch (Exception x) { + // We'll usually receive RuntimeException types here + // Need to unwrap to ascertain real causing exception before we choose to ignore + // Ignore err-logging all transport-level/type exceptions + if (!isIgnorableException(x)) { + // Log the exception at error level and continue + LOGGER.error((x instanceof TException? "Thrift " : "") + "Error occurred during processing of message.", x); + } + } finally { + if (eventHandler != null) { + eventHandler.deleteContext(connectionContext, inputProtocol, outputProtocol); + } + if (inputTransport != null) { + inputTransport.close(); + } + if (outputTransport != null) { + outputTransport.close(); + } + if (client_.isOpen()) { + client_.close(); + } + } + } + + private boolean isIgnorableException(Exception x) { + TTransportException tTransportException = null; + + if (x instanceof TTransportException) { + tTransportException = (TTransportException)x; + } + else if (x.getCause() instanceof TTransportException) { + tTransportException = (TTransportException)x.getCause(); + } + + if (tTransportException != null) { + switch(tTransportException.getType()) { + case TTransportException.END_OF_FILE: + case TTransportException.TIMED_OUT: + return true; + } + } + return false; + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java new file mode 100644 index 000000000..038507e9c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java @@ -0,0 +1,744 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.transport.TNonblockingServerTransport; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TTransportException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.spi.SelectorProvider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.TimeUnit; + +/** + * A Half-Sync/Half-Async server with a separate pool of threads to handle + * non-blocking I/O. Accepts are handled on a single thread, and a configurable + * number of nonblocking selector threads manage reading and writing of client + * connections. A synchronous worker thread pool handles processing of requests. + * + * Performs better than TNonblockingServer/THsHaServer in multi-core + * environments when the the bottleneck is CPU on the single selector thread + * handling I/O. In addition, because the accept handling is decoupled from + * reads/writes and invocation, the server has better ability to handle back- + * pressure from new connections (e.g. stop accepting when busy). + * + * Like TNonblockingServer, it relies on the use of TFramedTransport. + */ +public class TThreadedSelectorServer extends AbstractNonblockingServer { + private static final Logger LOGGER = LoggerFactory.getLogger(TThreadedSelectorServer.class.getName()); + + public static class Args extends AbstractNonblockingServerArgs<Args> { + + /** The number of threads for selecting on already-accepted connections */ + public int selectorThreads = 2; + /** + * The size of the executor service (if none is specified) that will handle + * invocations. This may be set to 0, in which case invocations will be + * handled directly on the selector threads (as is in TNonblockingServer) + */ + private int workerThreads = 5; + /** Time to wait for server to stop gracefully */ + private int stopTimeoutVal = 60; + private TimeUnit stopTimeoutUnit = TimeUnit.SECONDS; + /** The ExecutorService for handling dispatched requests */ + private ExecutorService executorService = null; + /** + * The size of the blocking queue per selector thread for passing accepted + * connections to the selector thread + */ + private int acceptQueueSizePerThread = 4; + + /** + * Determines the strategy for handling new accepted connections. + */ + public static enum AcceptPolicy { + /** + * Require accepted connection registration to be handled by the executor. + * If the worker pool is saturated, further accepts will be closed + * immediately. Slightly increases latency due to an extra scheduling. + */ + FAIR_ACCEPT, + /** + * Handle the accepts as fast as possible, disregarding the status of the + * executor service. + */ + FAST_ACCEPT + } + + private AcceptPolicy acceptPolicy = AcceptPolicy.FAST_ACCEPT; + + public Args(TNonblockingServerTransport transport) { + super(transport); + } + + public Args selectorThreads(int i) { + selectorThreads = i; + return this; + } + + public int getSelectorThreads() { + return selectorThreads; + } + + public Args workerThreads(int i) { + workerThreads = i; + return this; + } + + public int getWorkerThreads() { + return workerThreads; + } + + public int getStopTimeoutVal() { + return stopTimeoutVal; + } + + public Args stopTimeoutVal(int stopTimeoutVal) { + this.stopTimeoutVal = stopTimeoutVal; + return this; + } + + public TimeUnit getStopTimeoutUnit() { + return stopTimeoutUnit; + } + + public Args stopTimeoutUnit(TimeUnit stopTimeoutUnit) { + this.stopTimeoutUnit = stopTimeoutUnit; + return this; + } + + public ExecutorService getExecutorService() { + return executorService; + } + + public Args executorService(ExecutorService executorService) { + this.executorService = executorService; + return this; + } + + public int getAcceptQueueSizePerThread() { + return acceptQueueSizePerThread; + } + + public Args acceptQueueSizePerThread(int acceptQueueSizePerThread) { + this.acceptQueueSizePerThread = acceptQueueSizePerThread; + return this; + } + + public AcceptPolicy getAcceptPolicy() { + return acceptPolicy; + } + + public Args acceptPolicy(AcceptPolicy acceptPolicy) { + this.acceptPolicy = acceptPolicy; + return this; + } + + public void validate() { + if (selectorThreads <= 0) { + throw new IllegalArgumentException("selectorThreads must be positive."); + } + if (workerThreads < 0) { + throw new IllegalArgumentException("workerThreads must be non-negative."); + } + if (acceptQueueSizePerThread <= 0) { + throw new IllegalArgumentException("acceptQueueSizePerThread must be positive."); + } + } + } + + // The thread handling all accepts + private AcceptThread acceptThread; + + // Threads handling events on client transports + private final Set<SelectorThread> selectorThreads = new HashSet<SelectorThread>(); + + // This wraps all the functionality of queueing and thread pool management + // for the passing of Invocations from the selector thread(s) to the workers + // (if any). + private final ExecutorService invoker; + + private final Args args; + + /** + * Create the server with the specified Args configuration + */ + public TThreadedSelectorServer(Args args) { + super(args); + args.validate(); + invoker = args.executorService == null ? createDefaultExecutor(args) : args.executorService; + this.args = args; + } + + /** + * Start the accept and selector threads running to deal with clients. + * + * @return true if everything went ok, false if we couldn't start for some + * reason. + */ + @Override + protected boolean startThreads() { + try { + for (int i = 0; i < args.selectorThreads; ++i) { + selectorThreads.add(new SelectorThread(args.acceptQueueSizePerThread)); + } + acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_, + createSelectorThreadLoadBalancer(selectorThreads)); + for (SelectorThread thread : selectorThreads) { + thread.start(); + } + acceptThread.start(); + return true; + } catch (IOException e) { + LOGGER.error("Failed to start threads!", e); + return false; + } + } + + /** + * Joins the accept and selector threads and shuts down the executor service. + */ + @Override + protected void waitForShutdown() { + try { + joinThreads(); + } catch (InterruptedException e) { + // Non-graceful shutdown occurred + LOGGER.error("Interrupted while joining threads!", e); + } + gracefullyShutdownInvokerPool(); + } + + protected void joinThreads() throws InterruptedException { + // wait until the io threads exit + acceptThread.join(); + for (SelectorThread thread : selectorThreads) { + thread.join(); + } + } + + /** + * Stop serving and shut everything down. + */ + @Override + public void stop() { + stopped_ = true; + + // Stop queuing connect attempts asap + stopListening(); + + if (acceptThread != null) { + acceptThread.wakeupSelector(); + } + if (selectorThreads != null) { + for (SelectorThread thread : selectorThreads) { + if (thread != null) + thread.wakeupSelector(); + } + } + } + + protected void gracefullyShutdownInvokerPool() { + // try to gracefully shut down the executor service + invoker.shutdown(); + + // Loop until awaitTermination finally does return without a interrupted + // exception. If we don't do this, then we'll shut down prematurely. We want + // to let the executorService clear it's task queue, closing client sockets + // appropriately. + long timeoutMS = args.stopTimeoutUnit.toMillis(args.stopTimeoutVal); + long now = System.currentTimeMillis(); + while (timeoutMS >= 0) { + try { + invoker.awaitTermination(timeoutMS, TimeUnit.MILLISECONDS); + break; + } catch (InterruptedException ix) { + long newnow = System.currentTimeMillis(); + timeoutMS -= (newnow - now); + now = newnow; + } + } + } + + /** + * We override the standard invoke method here to queue the invocation for + * invoker service instead of immediately invoking. If there is no thread + * pool, handle the invocation inline on this thread + */ + @Override + protected boolean requestInvoke(FrameBuffer frameBuffer) { + Runnable invocation = getRunnable(frameBuffer); + if (invoker != null) { + try { + invoker.execute(invocation); + return true; + } catch (RejectedExecutionException rx) { + LOGGER.warn("ExecutorService rejected execution!", rx); + return false; + } + } else { + // Invoke on the caller's thread + invocation.run(); + return true; + } + } + + protected Runnable getRunnable(FrameBuffer frameBuffer) { + return new Invocation(frameBuffer); + } + + /** + * Helper to create the invoker if one is not specified + */ + protected static ExecutorService createDefaultExecutor(Args options) { + return (options.workerThreads > 0) ? Executors.newFixedThreadPool(options.workerThreads) : null; + } + + private static BlockingQueue<TNonblockingTransport> createDefaultAcceptQueue(int queueSize) { + if (queueSize == 0) { + // Unbounded queue + return new LinkedBlockingQueue<TNonblockingTransport>(); + } + return new ArrayBlockingQueue<TNonblockingTransport>(queueSize); + } + + /** + * The thread that selects on the server transport (listen socket) and accepts + * new connections to hand off to the IO selector threads + */ + protected class AcceptThread extends Thread { + + // The listen socket to accept on + private final TNonblockingServerTransport serverTransport; + private final Selector acceptSelector; + + private final SelectorThreadLoadBalancer threadChooser; + + /** + * Set up the AcceptThead + * + * @throws IOException + */ + public AcceptThread(TNonblockingServerTransport serverTransport, + SelectorThreadLoadBalancer threadChooser) throws IOException { + this.serverTransport = serverTransport; + this.threadChooser = threadChooser; + this.acceptSelector = SelectorProvider.provider().openSelector(); + this.serverTransport.registerSelector(acceptSelector); + } + + /** + * The work loop. Selects on the server transport and accepts. If there was + * a server transport that had blocking accepts, and returned on blocking + * client transports, that should be used instead + */ + public void run() { + try { + if (eventHandler_ != null) { + eventHandler_.preServe(); + } + + while (!stopped_) { + select(); + } + } catch (Throwable t) { + LOGGER.error("run() on AcceptThread exiting due to uncaught error", t); + } finally { + try { + acceptSelector.close(); + } catch (IOException e) { + LOGGER.error("Got an IOException while closing accept selector!", e); + } + // This will wake up the selector threads + TThreadedSelectorServer.this.stop(); + } + } + + /** + * If the selector is blocked, wake it up. + */ + public void wakeupSelector() { + acceptSelector.wakeup(); + } + + /** + * Select and process IO events appropriately: If there are connections to + * be accepted, accept them. + */ + private void select() { + try { + // wait for connect events. + acceptSelector.select(); + + // process the io events we received + Iterator<SelectionKey> selectedKeys = acceptSelector.selectedKeys().iterator(); + while (!stopped_ && selectedKeys.hasNext()) { + SelectionKey key = selectedKeys.next(); + selectedKeys.remove(); + + // skip if not valid + if (!key.isValid()) { + continue; + } + + if (key.isAcceptable()) { + handleAccept(); + } else { + LOGGER.warn("Unexpected state in select! " + key.interestOps()); + } + } + } catch (IOException e) { + LOGGER.warn("Got an IOException while selecting!", e); + } + } + + /** + * Accept a new connection. + */ + private void handleAccept() { + final TNonblockingTransport client = doAccept(); + if (client != null) { + // Pass this connection to a selector thread + final SelectorThread targetThread = threadChooser.nextThread(); + + if (args.acceptPolicy == Args.AcceptPolicy.FAST_ACCEPT || invoker == null) { + doAddAccept(targetThread, client); + } else { + // FAIR_ACCEPT + try { + invoker.submit(new Runnable() { + public void run() { + doAddAccept(targetThread, client); + } + }); + } catch (RejectedExecutionException rx) { + LOGGER.warn("ExecutorService rejected accept registration!", rx); + // close immediately + client.close(); + } + } + } + } + + private TNonblockingTransport doAccept() { + try { + return (TNonblockingTransport) serverTransport.accept(); + } catch (TTransportException tte) { + // something went wrong accepting. + LOGGER.warn("Exception trying to accept!", tte); + return null; + } + } + + private void doAddAccept(SelectorThread thread, TNonblockingTransport client) { + if (!thread.addAcceptedConnection(client)) { + client.close(); + } + } + } // AcceptThread + + /** + * The SelectorThread(s) will be doing all the selecting on accepted active + * connections. + */ + protected class SelectorThread extends AbstractSelectThread { + + // Accepted connections added by the accept thread. + private final BlockingQueue<TNonblockingTransport> acceptedQueue; + private int SELECTOR_AUTO_REBUILD_THRESHOLD = 512; + private long MONITOR_PERIOD = 1000L; + private int jvmBug = 0; + + /** + * Set up the SelectorThread with an unbounded queue for incoming accepts. + * + * @throws IOException + * if a selector cannot be created + */ + public SelectorThread() throws IOException { + this(new LinkedBlockingQueue<TNonblockingTransport>()); + } + + /** + * Set up the SelectorThread with an bounded queue for incoming accepts. + * + * @throws IOException + * if a selector cannot be created + */ + public SelectorThread(int maxPendingAccepts) throws IOException { + this(createDefaultAcceptQueue(maxPendingAccepts)); + } + + /** + * Set up the SelectorThread with a specified queue for connections. + * + * @param acceptedQueue + * The BlockingQueue implementation for holding incoming accepted + * connections. + * @throws IOException + * if a selector cannot be created. + */ + public SelectorThread(BlockingQueue<TNonblockingTransport> acceptedQueue) throws IOException { + this.acceptedQueue = acceptedQueue; + } + + /** + * Hands off an accepted connection to be handled by this thread. This + * method will block if the queue for new connections is at capacity. + * + * @param accepted + * The connection that has been accepted. + * @return true if the connection has been successfully added. + */ + public boolean addAcceptedConnection(TNonblockingTransport accepted) { + try { + acceptedQueue.put(accepted); + } catch (InterruptedException e) { + LOGGER.warn("Interrupted while adding accepted connection!", e); + return false; + } + selector.wakeup(); + return true; + } + + /** + * The work loop. Handles selecting (read/write IO), dispatching, and + * managing the selection preferences of all existing connections. + */ + public void run() { + try { + while (!stopped_) { + select(); + processAcceptedConnections(); + processInterestChanges(); + } + for (SelectionKey selectionKey : selector.keys()) { + cleanupSelectionKey(selectionKey); + } + } catch (Throwable t) { + LOGGER.error("run() on SelectorThread exiting due to uncaught error", t); + } finally { + try { + selector.close(); + } catch (IOException e) { + LOGGER.error("Got an IOException while closing selector!", e); + } + // This will wake up the accept thread and the other selector threads + TThreadedSelectorServer.this.stop(); + } + } + + /** + * Select and process IO events appropriately: If there are existing + * connections with data waiting to be read, read it, buffering until a + * whole frame has been read. If there are any pending responses, buffer + * them until their target client is available, and then send the data. + */ + private void select() { + try { + + doSelect(); + + // process the io events we received + Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator(); + while (!stopped_ && selectedKeys.hasNext()) { + SelectionKey key = selectedKeys.next(); + selectedKeys.remove(); + + // skip if not valid + if (!key.isValid()) { + cleanupSelectionKey(key); + continue; + } + + if (key.isReadable()) { + // deal with reads + handleRead(key); + } else if (key.isWritable()) { + // deal with writes + handleWrite(key); + } else { + LOGGER.warn("Unexpected state in select! " + key.interestOps()); + } + } + } catch (IOException e) { + LOGGER.warn("Got an IOException while selecting!", e); + } + } + + /** + * Do select and judge epoll bug happen. + * See : https://issues.apache.org/jira/browse/THRIFT-4251 + */ + private void doSelect() throws IOException { + long beforeSelect = System.currentTimeMillis(); + int selectedNums = selector.select(); + long afterSelect = System.currentTimeMillis(); + + if (selectedNums == 0) { + jvmBug++; + } else { + jvmBug = 0; + } + + long selectedTime = afterSelect - beforeSelect; + if (selectedTime >= MONITOR_PERIOD) { + jvmBug = 0; + } else if (jvmBug > SELECTOR_AUTO_REBUILD_THRESHOLD) { + LOGGER.warn("In {} ms happen {} times jvm bug; rebuilding selector.", MONITOR_PERIOD, jvmBug); + rebuildSelector(); + selector.selectNow(); + jvmBug = 0; + } + + } + + /** + * Replaces the current Selector of this SelectorThread with newly created Selector to work + * around the infamous epoll 100% CPU bug. + */ + private synchronized void rebuildSelector() { + final Selector oldSelector = selector; + if (oldSelector == null) { + return; + } + Selector newSelector = null; + try { + newSelector = Selector.open(); + LOGGER.warn("Created new Selector."); + } catch (IOException e) { + LOGGER.error("Create new Selector error.", e); + } + + for (SelectionKey key : oldSelector.selectedKeys()) { + if (!key.isValid() && key.readyOps() == 0) + continue; + SelectableChannel channel = key.channel(); + Object attachment = key.attachment(); + + try { + if (attachment == null) { + channel.register(newSelector, key.readyOps()); + } else { + channel.register(newSelector, key.readyOps(), attachment); + } + } catch (ClosedChannelException e) { + LOGGER.error("Register new selector key error.", e); + } + + } + + selector = newSelector; + try { + oldSelector.close(); + } catch (IOException e) { + LOGGER.error("Close old selector error.", e); + } + LOGGER.warn("Replace new selector success."); + } + + private void processAcceptedConnections() { + // Register accepted connections + while (!stopped_) { + TNonblockingTransport accepted = acceptedQueue.poll(); + if (accepted == null) { + break; + } + registerAccepted(accepted); + } + } + + protected FrameBuffer createFrameBuffer(final TNonblockingTransport trans, + final SelectionKey selectionKey, + final AbstractSelectThread selectThread) { + return processorFactory_.isAsyncProcessor() ? + new AsyncFrameBuffer(trans, selectionKey, selectThread) : + new FrameBuffer(trans, selectionKey, selectThread); + } + + private void registerAccepted(TNonblockingTransport accepted) { + SelectionKey clientKey = null; + try { + clientKey = accepted.registerSelector(selector, SelectionKey.OP_READ); + + FrameBuffer frameBuffer = createFrameBuffer(accepted, clientKey, SelectorThread.this); + + clientKey.attach(frameBuffer); + } catch (IOException e) { + LOGGER.warn("Failed to register accepted connection to selector!", e); + if (clientKey != null) { + cleanupSelectionKey(clientKey); + } + accepted.close(); + } + } + } // SelectorThread + + /** + * Creates a SelectorThreadLoadBalancer to be used by the accept thread for + * assigning newly accepted connections across the threads. + */ + protected SelectorThreadLoadBalancer createSelectorThreadLoadBalancer(Collection<? extends SelectorThread> threads) { + return new SelectorThreadLoadBalancer(threads); + } + + /** + * A round robin load balancer for choosing selector threads for new + * connections. + */ + protected static class SelectorThreadLoadBalancer { + private final Collection<? extends SelectorThread> threads; + private Iterator<? extends SelectorThread> nextThreadIterator; + + public <T extends SelectorThread> SelectorThreadLoadBalancer(Collection<T> threads) { + if (threads.isEmpty()) { + throw new IllegalArgumentException("At least one selector thread is required"); + } + this.threads = Collections.unmodifiableList(new ArrayList<T>(threads)); + nextThreadIterator = this.threads.iterator(); + } + + public SelectorThread nextThread() { + // Choose a selector thread (round robin) + if (!nextThreadIterator.hasNext()) { + nextThreadIterator = threads.iterator(); + } + return nextThreadIterator.next(); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java new file mode 100644 index 000000000..fc3aa92df --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBuffer.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.util.Arrays; + +/** + * Helper class that wraps a byte[] so that it can expand and be reused. Users + * should call resizeIfNecessary to make sure the buffer has suitable capacity, + * and then use the array as needed. Note that the internal array will grow at a + * rate slightly faster than the requested capacity with the (untested) + * objective of avoiding expensive buffer allocations and copies. + */ +class AutoExpandingBuffer { + private byte[] array; + + public AutoExpandingBuffer(int initialCapacity) { + this.array = new byte[initialCapacity]; + } + + public void resizeIfNecessary(int size) { + final int currentCapacity = this.array.length; + if (currentCapacity < size) { + // Increase by a factor of 1.5x + int growCapacity = currentCapacity + (currentCapacity >> 1); + int newCapacity = Math.max(growCapacity, size); + this.array = Arrays.copyOf(array, newCapacity); + } + } + + public byte[] array() { + return this.array; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java new file mode 100644 index 000000000..a28d25485 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferReadTransport.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +/** + * TTransport for reading from an AutoExpandingBuffer. + */ +public class AutoExpandingBufferReadTransport extends TTransport { + + private final AutoExpandingBuffer buf; + + private int pos = 0; + private int limit = 0; + + public AutoExpandingBufferReadTransport(int initialCapacity) { + this.buf = new AutoExpandingBuffer(initialCapacity); + } + + public void fill(TTransport inTrans, int length) throws TTransportException { + buf.resizeIfNecessary(length); + inTrans.readAll(buf.array(), 0, length); + pos = 0; + limit = length; + } + + @Override + public void close() {} + + @Override + public boolean isOpen() { return true; } + + @Override + public void open() throws TTransportException {} + + @Override + public final int read(byte[] target, int off, int len) throws TTransportException { + int amtToRead = Math.min(len, getBytesRemainingInBuffer()); + System.arraycopy(buf.array(), pos, target, off, amtToRead); + consumeBuffer(amtToRead); + return amtToRead; + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + throw new UnsupportedOperationException(); + } + + @Override + public final void consumeBuffer(int len) { + pos += len; + } + + @Override + public final byte[] getBuffer() { + return buf.array(); + } + + @Override + public final int getBufferPosition() { + return pos; + } + + @Override + public final int getBytesRemainingInBuffer() { + return limit - pos; + } +} +
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java new file mode 100644 index 000000000..ec7e7d45a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/AutoExpandingBufferWriteTransport.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +/** + * TTransport for writing to an AutoExpandingBuffer. + */ +public final class AutoExpandingBufferWriteTransport extends TTransport { + + private final AutoExpandingBuffer buf; + private int pos; + private int res; + + /** + * Constructor. + * @param initialCapacity the initial capacity of the buffer + * @param frontReserve space, if any, to reserve at the beginning such + * that the first write is after this reserve. + * This allows framed transport to reserve space + * for the frame buffer length. + * @throws IllegalArgumentException if initialCapacity is less than one + * @throws IllegalArgumentException if frontReserve is less than zero + * @throws IllegalArgumentException if frontReserve is greater than initialCapacity + */ + public AutoExpandingBufferWriteTransport(int initialCapacity, int frontReserve) { + if (initialCapacity < 1) { + throw new IllegalArgumentException("initialCapacity"); + } + if (frontReserve < 0 || initialCapacity < frontReserve) { + throw new IllegalArgumentException("frontReserve"); + } + this.buf = new AutoExpandingBuffer(initialCapacity); + this.pos = frontReserve; + this.res = frontReserve; + } + + @Override + public void close() {} + + @Override + public boolean isOpen() {return true;} + + @Override + public void open() throws TTransportException {} + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + throw new UnsupportedOperationException(); + } + + @Override + public void write(byte[] toWrite, int off, int len) throws TTransportException { + buf.resizeIfNecessary(pos + len); + System.arraycopy(toWrite, off, buf.array(), pos, len); + pos += len; + } + + public AutoExpandingBuffer getBuf() { + return buf; + } + + /** + * @return length of the buffer, including any front reserve + */ + public int getLength() { + return pos; + } + + public void reset() { + pos = res; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java new file mode 100644 index 000000000..b6b065748 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TByteBuffer.java @@ -0,0 +1,87 @@ +package org.apache.thrift.transport; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; + +/** + * ByteBuffer-backed implementation of TTransport. + */ +public final class TByteBuffer extends TTransport { + private final ByteBuffer byteBuffer; + + /** + * Creates a new TByteBuffer wrapping a given NIO ByteBuffer. + */ + public TByteBuffer(ByteBuffer byteBuffer) { + this.byteBuffer = byteBuffer; + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + public void open() { + } + + @Override + public void close() { + } + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + final int n = Math.min(byteBuffer.remaining(), len); + if (n > 0) { + try { + byteBuffer.get(buf, off, n); + } catch (BufferUnderflowException e) { + throw new TTransportException("Unexpected end of input buffer", e); + } + } + return n; + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + try { + byteBuffer.put(buf, off, len); + } catch (BufferOverflowException e) { + throw new TTransportException("Not enough room in output buffer", e); + } + } + + /** + * Get the underlying NIO ByteBuffer. + */ + public ByteBuffer getByteBuffer() { + return byteBuffer; + } + + /** + * Convenience method to call clear() on the underlying NIO ByteBuffer. + */ + public TByteBuffer clear() { + byteBuffer.clear(); + return this; + } + + /** + * Convenience method to call flip() on the underlying NIO ByteBuffer. + */ + public TByteBuffer flip() { + byteBuffer.flip(); + return this; + } + + /** + * Convenience method to convert the underlying NIO ByteBuffer to a + * plain old byte array. + */ + public byte[] toByteArray() { + final byte[] data = new byte[byteBuffer.remaining()]; + byteBuffer.slice().get(data); + return data; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java new file mode 100644 index 000000000..a1fd2490a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFastFramedTransport.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +/** + * This transport is wire compatible with {@link TFramedTransport}, but makes + * use of reusable, expanding read and write buffers in order to avoid + * allocating new byte[]s all the time. Since the buffers only expand, you + * should probably only use this transport if your messages are not too variably + * large, unless the persistent memory cost is not an issue. + * + * This implementation is NOT threadsafe. + */ +public class TFastFramedTransport extends TTransport { + + public static class Factory extends TTransportFactory { + private final int initialCapacity; + private final int maxLength; + + public Factory() { + this(DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH); + } + + public Factory(int initialCapacity) { + this(initialCapacity, DEFAULT_MAX_LENGTH); + } + + public Factory(int initialCapacity, int maxLength) { + this.initialCapacity = initialCapacity; + this.maxLength = maxLength; + } + + @Override + public TTransport getTransport(TTransport trans) { + return new TFastFramedTransport(trans, + initialCapacity, + maxLength); + } + } + + /** + * How big should the default read and write buffers be? + */ + public static final int DEFAULT_BUF_CAPACITY = 1024; + /** + * How big is the largest allowable frame? Defaults to 16MB. + */ + public static final int DEFAULT_MAX_LENGTH = 16384000; + + private final TTransport underlying; + private final AutoExpandingBufferWriteTransport writeBuffer; + private AutoExpandingBufferReadTransport readBuffer; + private final int initialBufferCapacity; + private final byte[] i32buf = new byte[4]; + private final int maxLength; + + /** + * Create a new {@link TFastFramedTransport}. Use the defaults + * for initial buffer size and max frame length. + * @param underlying Transport that real reads and writes will go through to. + */ + public TFastFramedTransport(TTransport underlying) { + this(underlying, DEFAULT_BUF_CAPACITY, DEFAULT_MAX_LENGTH); + } + + /** + * Create a new {@link TFastFramedTransport}. Use the specified + * initial buffer capacity and the default max frame length. + * @param underlying Transport that real reads and writes will go through to. + * @param initialBufferCapacity The initial size of the read and write buffers. + * In practice, it's not critical to set this unless you know in advance that + * your messages are going to be very large. + */ + public TFastFramedTransport(TTransport underlying, int initialBufferCapacity) { + this(underlying, initialBufferCapacity, DEFAULT_MAX_LENGTH); + } + + /** + * + * @param underlying Transport that real reads and writes will go through to. + * @param initialBufferCapacity The initial size of the read and write buffers. + * In practice, it's not critical to set this unless you know in advance that + * your messages are going to be very large. (You can pass + * TFramedTransportWithReusableBuffer.DEFAULT_BUF_CAPACITY if you're only + * using this constructor because you want to set the maxLength.) + * @param maxLength The max frame size you are willing to read. You can use + * this parameter to limit how much memory can be allocated. + */ + public TFastFramedTransport(TTransport underlying, int initialBufferCapacity, int maxLength) { + this.underlying = underlying; + this.maxLength = maxLength; + this.initialBufferCapacity = initialBufferCapacity; + readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity); + writeBuffer = new AutoExpandingBufferWriteTransport(initialBufferCapacity, 4); + } + + @Override + public void close() { + underlying.close(); + } + + @Override + public boolean isOpen() { + return underlying.isOpen(); + } + + @Override + public void open() throws TTransportException { + underlying.open(); + } + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + int got = readBuffer.read(buf, off, len); + if (got > 0) { + return got; + } + + // Read another frame of data + readFrame(); + + return readBuffer.read(buf, off, len); + } + + private void readFrame() throws TTransportException { + underlying.readAll(i32buf , 0, 4); + int size = TFramedTransport.decodeFrameSize(i32buf); + + if (size < 0) { + close(); + throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!"); + } + + if (size > maxLength) { + close(); + throw new TTransportException(TTransportException.CORRUPTED_DATA, + "Frame size (" + size + ") larger than max length (" + maxLength + ")!"); + } + + readBuffer.fill(underlying, size); + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + writeBuffer.write(buf, off, len); + } + + @Override + public void consumeBuffer(int len) { + readBuffer.consumeBuffer(len); + } + + /** + * Only clears the read buffer! + */ + public void clear() { + readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity); + } + + @Override + public void flush() throws TTransportException { + int payloadLength = writeBuffer.getLength() - 4; + byte[] data = writeBuffer.getBuf().array(); + TFramedTransport.encodeFrameSize(payloadLength, data); + underlying.write(data, 0, payloadLength + 4); + writeBuffer.reset(); + underlying.flush(); + } + + @Override + public byte[] getBuffer() { + return readBuffer.getBuffer(); + } + + @Override + public int getBufferPosition() { + return readBuffer.getBufferPosition(); + } + + @Override + public int getBytesRemainingInBuffer() { + return readBuffer.getBytesRemainingInBuffer(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java new file mode 100644 index 000000000..96087d1a3 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileProcessor.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; + +/** + * FileProcessor: helps in processing files generated by TFileTransport. + * Port of original cpp implementation + */ +public class TFileProcessor { + + private TProcessor processor_; + private TProtocolFactory inputProtocolFactory_; + private TProtocolFactory outputProtocolFactory_; + private TFileTransport inputTransport_; + private TTransport outputTransport_; + + public TFileProcessor(TProcessor processor, TProtocolFactory protocolFactory, + TFileTransport inputTransport, + TTransport outputTransport) { + processor_ = processor; + inputProtocolFactory_ = outputProtocolFactory_ = protocolFactory; + inputTransport_ = inputTransport; + outputTransport_ = outputTransport; + } + + public TFileProcessor(TProcessor processor, + TProtocolFactory inputProtocolFactory, + TProtocolFactory outputProtocolFactory, + TFileTransport inputTransport, + TTransport outputTransport) { + processor_ = processor; + inputProtocolFactory_ = inputProtocolFactory; + outputProtocolFactory_ = outputProtocolFactory; + inputTransport_ = inputTransport; + outputTransport_ = outputTransport; + } + + private void processUntil(int lastChunk) throws TException { + TProtocol ip = inputProtocolFactory_.getProtocol(inputTransport_); + TProtocol op = outputProtocolFactory_.getProtocol(outputTransport_); + int curChunk = inputTransport_.getCurChunk(); + + try { + while (lastChunk >= curChunk) { + processor_.process(ip, op); + int newChunk = inputTransport_.getCurChunk(); + curChunk = newChunk; + } + } catch (TTransportException e) { + // if we are processing the last chunk - we could have just hit EOF + // on EOF - trap the error and stop processing. + if(e.getType() != TTransportException.END_OF_FILE) + throw e; + else { + return; + } + } + } + + /** + * Process from start to last chunk both inclusive where chunks begin from 0 + + * @param startChunkNum first chunk to be processed + * @param endChunkNum last chunk to be processed + */ + public void processChunk(int startChunkNum, int endChunkNum) throws TException { + int numChunks = inputTransport_.getNumChunks(); + if(endChunkNum < 0) + endChunkNum += numChunks; + + if(startChunkNum < 0) + startChunkNum += numChunks; + + if(endChunkNum < startChunkNum) + throw new TException("endChunkNum " + endChunkNum + " is less than " + startChunkNum); + + inputTransport_.seekToChunk(startChunkNum); + processUntil(endChunkNum); + } + + /** + * Process a single chunk + * + * @param chunkNum chunk to be processed + */ + public void processChunk(int chunkNum) throws TException { + processChunk(chunkNum, chunkNum); + } + + /** + * Process a current chunk + */ + public void processChunk() throws TException { + processChunk(inputTransport_.getCurChunk()); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java new file mode 100644 index 000000000..88b73e54d --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFileTransport.java @@ -0,0 +1,624 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.util.Random; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FileTransport implementation of the TTransport interface. + * Currently this is a straightforward port of the cpp implementation + * + * It may make better sense to provide a basic stream access on top of the framed file format + * The FileTransport can then be a user of this framed file format with some additional logic + * for chunking. + */ +public class TFileTransport extends TTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TFileTransport.class.getName()); + + public static class TruncableBufferedInputStream extends BufferedInputStream { + public void trunc() { + pos = count = 0; + } + public TruncableBufferedInputStream(InputStream in) { + super(in); + } + public TruncableBufferedInputStream(InputStream in, int size) { + super(in, size); + } + } + + + public static class Event { + private byte[] buf_; + private int nread_; + private int navailable_; + + /** + * Initialize an event. Initially, it has no valid contents + * + * @param buf byte array buffer to store event + */ + public Event(byte[] buf) { + buf_ = buf; + nread_ = navailable_ = 0; + } + + public byte[] getBuf() { return buf_;} + public int getSize() { return buf_.length; } + + + public void setAvailable(int sz) { nread_ = 0; navailable_=sz;} + public int getRemaining() { return (navailable_ - nread_); } + + public int emit(byte[] buf, int offset, int ndesired) { + if((ndesired == 0) || (ndesired > getRemaining())) + ndesired = getRemaining(); + + if(ndesired <= 0) + return (ndesired); + + System.arraycopy(buf_, nread_, buf, offset, ndesired); + nread_ += ndesired; + + return(ndesired); + } + }; + + public static class ChunkState { + /** + * Chunk Size. Must be same across all implementations + */ + public static final int DEFAULT_CHUNK_SIZE = 16 * 1024 * 1024; + + private int chunk_size_ = DEFAULT_CHUNK_SIZE; + private long offset_ = 0; + + public ChunkState() {} + public ChunkState(int chunk_size) { chunk_size_ = chunk_size; } + + public void skip(int size) {offset_ += size; } + public void seek(long offset) {offset_ = offset;} + + public int getChunkSize() { return chunk_size_;} + public int getChunkNum() { return ((int)(offset_/chunk_size_));} + public int getRemaining() { return (chunk_size_ - ((int)(offset_ % chunk_size_)));} + public long getOffset() { return (offset_);} + } + + public static enum TailPolicy { + + NOWAIT(0, 0), + WAIT_FOREVER(500, -1); + + /** + * Time in milliseconds to sleep before next read + * If 0, no sleep + */ + public final int timeout_; + + /** + * Number of retries before giving up + * if 0, no retries + * if -1, retry forever + */ + public final int retries_; + + /** + * ctor for policy + * + * @param timeout sleep time for this particular policy + * @param retries number of retries + */ + + TailPolicy(int timeout, int retries) { + timeout_ = timeout; + retries_ = retries; + } + } + + /** + * Current tailing policy + */ + TailPolicy currentPolicy_ = TailPolicy.NOWAIT; + + + /** + * Underlying file being read + */ + protected TSeekableFile inputFile_ = null; + + /** + * Underlying outputStream + */ + protected OutputStream outputStream_ = null; + + + /** + * Event currently read in + */ + Event currentEvent_ = null; + + /** + * InputStream currently being used for reading + */ + InputStream inputStream_ = null; + + /** + * current Chunk state + */ + ChunkState cs = null; + + /** + * is read only? + */ + private boolean readOnly_ = false; + + /** + * Get File Tailing Policy + * + * @return current read policy + */ + public TailPolicy getTailPolicy() { + return (currentPolicy_); + } + + /** + * Set file Tailing Policy + * + * @param policy New policy to set + * @return Old policy + */ + public TailPolicy setTailPolicy(TailPolicy policy) { + TailPolicy old = currentPolicy_; + currentPolicy_ = policy; + return (old); + } + + + /** + * Initialize read input stream + * + * @return input stream to read from file + */ + private InputStream createInputStream() throws TTransportException { + InputStream is; + try { + if(inputStream_ != null) { + ((TruncableBufferedInputStream)inputStream_).trunc(); + is = inputStream_; + } else { + is = new TruncableBufferedInputStream(inputFile_.getInputStream()); + } + } catch (IOException iox) { + throw new TTransportException(iox.getMessage(), iox); + } + return(is); + } + + /** + * Read (potentially tailing) an input stream + * + * @param is InputStream to read from + * @param buf Buffer to read into + * @param off Offset in buffer to read into + * @param len Number of bytes to read + * @param tp policy to use if we hit EOF + * + * @return number of bytes read + */ + private int tailRead(InputStream is, byte[] buf, + int off, int len, TailPolicy tp) throws TTransportException { + int orig_len = len; + try { + int retries = 0; + while(len > 0) { + int cnt = is.read(buf, off, len); + if(cnt > 0) { + off += cnt; + len -= cnt; + retries = 0; + cs.skip(cnt); // remember that we read so many bytes + } else if (cnt == -1) { + // EOF + retries++; + + if((tp.retries_ != -1) && tp.retries_ < retries) + return (orig_len - len); + + if(tp.timeout_ > 0) { + try {Thread.sleep(tp.timeout_);} catch(InterruptedException e) {} + } + } else { + // either non-zero or -1 is what the contract says! + throw new + TTransportException("Unexpected return from InputStream.read = " + + cnt); + } + } + } catch (IOException iox) { + throw new TTransportException(iox.getMessage(), iox); + } + + return(orig_len - len); + } + + /** + * Event is corrupted. Do recovery + * + * @return true if recovery could be performed and we can read more data + * false is returned only when nothing more can be read + */ + private boolean performRecovery() throws TTransportException { + int numChunks = getNumChunks(); + int curChunk = cs.getChunkNum(); + + if(curChunk >= (numChunks-1)) { + return false; + } + seekToChunk(curChunk+1); + return true; + } + + /** + * Read event from underlying file + * + * @return true if event could be read, false otherwise (on EOF) + */ + private boolean readEvent() throws TTransportException { + byte[] ebytes = new byte[4]; + int esize; + int nread; + int nrequested; + + retry: + do { + // corner case. read to end of chunk + nrequested = cs.getRemaining(); + if(nrequested < 4) { + nread = tailRead(inputStream_, ebytes, 0, nrequested, currentPolicy_); + if(nread != nrequested) { + return(false); + } + } + + // assuming serialized on little endian machine + nread = tailRead(inputStream_, ebytes, 0, 4, currentPolicy_); + if(nread != 4) { + return(false); + } + + esize=0; + for(int i=3; i>=0; i--) { + int val = (0x000000ff & (int)ebytes[i]); + esize |= (val << (i*8)); + } + + // check if event is corrupted and do recovery as required + if(esize > cs.getRemaining()) { + throw new TTransportException("FileTransport error: bad event size"); + /* + if(performRecovery()) { + esize=0; + } else { + return false; + } + */ + } + } while (esize == 0); + + // reset existing event or get a larger one + if(currentEvent_.getSize() < esize) + currentEvent_ = new Event(new byte [esize]); + + // populate the event + byte[] buf = currentEvent_.getBuf(); + nread = tailRead(inputStream_, buf, 0, esize, currentPolicy_); + if(nread != esize) { + return(false); + } + currentEvent_.setAvailable(esize); + return(true); + } + + /** + * open if both input/output open unless readonly + * + * @return true + */ + public boolean isOpen() { + return ((inputStream_ != null) && (readOnly_ || (outputStream_ != null))); + } + + + /** + * Diverging from the cpp model and sticking to the TSocket model + * Files are not opened in ctor - but in explicit open call + */ + public void open() throws TTransportException { + if (isOpen()) + throw new TTransportException(TTransportException.ALREADY_OPEN); + + try { + inputStream_ = createInputStream(); + cs = new ChunkState(); + currentEvent_ = new Event(new byte [256]); + + if(!readOnly_) + outputStream_ = new BufferedOutputStream(inputFile_.getOutputStream()); + } catch (IOException iox) { + throw new TTransportException(TTransportException.NOT_OPEN, iox); + } + } + + /** + * Closes the transport. + */ + public void close() { + if (inputFile_ != null) { + try { + inputFile_.close(); + } catch (IOException iox) { + LOGGER.warn("WARNING: Error closing input file: " + + iox.getMessage()); + } + inputFile_ = null; + } + if (outputStream_ != null) { + try { + outputStream_.close(); + } catch (IOException iox) { + LOGGER.warn("WARNING: Error closing output stream: " + + iox.getMessage()); + } + outputStream_ = null; + } + } + + + /** + * File Transport ctor + * + * @param path File path to read and write from + * @param readOnly Whether this is a read-only transport + */ + public TFileTransport(final String path, boolean readOnly) throws IOException { + inputFile_ = new TStandardFile(path); + readOnly_ = readOnly; + } + + /** + * File Transport ctor + * + * @param inputFile open TSeekableFile to read/write from + * @param readOnly Whether this is a read-only transport + */ + public TFileTransport(TSeekableFile inputFile, boolean readOnly) { + inputFile_ = inputFile; + readOnly_ = readOnly; + } + + + /** + * Cloned from TTransport.java:readAll(). Only difference is throwing an EOF exception + * where one is detected. + */ + public int readAll(byte[] buf, int off, int len) + throws TTransportException { + int got = 0; + int ret = 0; + while (got < len) { + ret = read(buf, off+got, len-got); + if (ret < 0) { + throw new TTransportException("Error in reading from file"); + } + if(ret == 0) { + throw new TTransportException(TTransportException.END_OF_FILE, + "End of File reached"); + } + got += ret; + } + return got; + } + + + /** + * Reads up to len bytes into buffer buf, starting at offset off. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read + * @throws TTransportException if there was an error reading data + */ + public int read(byte[] buf, int off, int len) throws TTransportException { + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, + "Must open before reading"); + + if(currentEvent_.getRemaining() == 0) { + if(!readEvent()) + return(0); + } + + int nread = currentEvent_.emit(buf, off, len); + return nread; + } + + public int getNumChunks() throws TTransportException { + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, + "Must open before getNumChunks"); + try { + long len = inputFile_.length(); + if(len == 0) + return 0; + else + return (((int)(len/cs.getChunkSize())) + 1); + + } catch (IOException iox) { + throw new TTransportException(iox.getMessage(), iox); + } + } + + public int getCurChunk() throws TTransportException { + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, + "Must open before getCurChunk"); + return (cs.getChunkNum()); + + } + + + public void seekToChunk(int chunk) throws TTransportException { + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, + "Must open before seeking"); + + int numChunks = getNumChunks(); + + // file is empty, seeking to chunk is pointless + if (numChunks == 0) { + return; + } + + // negative indicates reverse seek (from the end) + if (chunk < 0) { + chunk += numChunks; + } + + // too large a value for reverse seek, just seek to beginning + if (chunk < 0) { + chunk = 0; + } + + long eofOffset=0; + boolean seekToEnd = (chunk >= numChunks); + if(seekToEnd) { + chunk = chunk - 1; + try { eofOffset = inputFile_.length(); } + catch (IOException iox) {throw new TTransportException(iox.getMessage(), + iox);} + } + + if(chunk*cs.getChunkSize() != cs.getOffset()) { + try { inputFile_.seek((long)chunk*cs.getChunkSize()); } + catch (IOException iox) { + throw new TTransportException("Seek to chunk " + + chunk + " " +iox.getMessage(), iox); + } + + cs.seek((long)chunk*cs.getChunkSize()); + currentEvent_.setAvailable(0); + inputStream_ = createInputStream(); + } + + if(seekToEnd) { + // waiting forever here - otherwise we can hit EOF and end up + // having consumed partial data from the data stream. + TailPolicy old = setTailPolicy(TailPolicy.WAIT_FOREVER); + while(cs.getOffset() < eofOffset) { readEvent(); } + currentEvent_.setAvailable(0); + setTailPolicy(old); + } + } + + public void seekToEnd() throws TTransportException { + if(!isOpen()) + throw new TTransportException(TTransportException.NOT_OPEN, + "Must open before seeking"); + seekToChunk(getNumChunks()); + } + + + /** + * Writes up to len bytes from the buffer. + * + * @param buf The output data buffer + * @param off The offset to start writing from + * @param len The number of bytes to write + * @throws TTransportException if there was an error writing data + */ + public void write(byte[] buf, int off, int len) throws TTransportException { + throw new TTransportException("Not Supported"); + } + + /** + * Flush any pending data out of a transport buffer. + * + * @throws TTransportException if there was an error writing out data. + */ + public void flush() throws TTransportException { + throw new TTransportException("Not Supported"); + } + + /** + * test program + * + */ + public static void main(String[] args) throws Exception { + + int num_chunks = 10; + + if((args.length < 1) || args[0].equals("--help") + || args[0].equals("-h") || args[0].equals("-?")) { + printUsage(); + } + + if(args.length > 1) { + try { + num_chunks = Integer.parseInt(args[1]); + } catch (Exception e) { + LOGGER.error("Cannot parse " + args[1]); + printUsage(); + } + } + + TFileTransport t = new TFileTransport(args[0], true); + t.open(); + LOGGER.info("NumChunks="+t.getNumChunks()); + + Random r = new Random(); + for(int j=0; j<num_chunks; j++) { + byte[] buf = new byte[4096]; + int cnum = r.nextInt(t.getNumChunks()-1); + LOGGER.info("Reading chunk "+cnum); + t.seekToChunk(cnum); + for(int i=0; i<4096; i++) { + t.read(buf, 0, 4096); + } + } + } + + private static void printUsage() { + LOGGER.error("Usage: TFileTransport <filename> [num_chunks]"); + LOGGER.error(" (Opens and reads num_chunks chunks from file randomly)"); + System.exit(1); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java new file mode 100644 index 000000000..a006c3a6a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TFramedTransport.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.apache.thrift.TByteArrayOutputStream; + +/** + * TFramedTransport is a buffered TTransport that ensures a fully read message + * every time by preceding messages with a 4-byte frame size. + */ +public class TFramedTransport extends TTransport { + + protected static final int DEFAULT_MAX_LENGTH = 16384000; + + private int maxLength_; + + /** + * Underlying transport + */ + private TTransport transport_ = null; + + /** + * Buffer for output + */ + private final TByteArrayOutputStream writeBuffer_ = + new TByteArrayOutputStream(1024); + + /** + * Buffer for input + */ + private final TMemoryInputTransport readBuffer_ = + new TMemoryInputTransport(new byte[0]); + + public static class Factory extends TTransportFactory { + private int maxLength_; + + public Factory() { + maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH; + } + + public Factory(int maxLength) { + maxLength_ = maxLength; + } + + @Override + public TTransport getTransport(TTransport base) { + return new TFramedTransport(base, maxLength_); + } + } + + /** + * Something to fill in the first four bytes of the buffer + * to make room for the frame size. This allows the + * implementation to write once instead of twice. + */ + private static final byte[] sizeFiller_ = new byte[] { 0x00, 0x00, 0x00, 0x00 }; + + /** + * Constructor wraps around another transport + */ + public TFramedTransport(TTransport transport, int maxLength) { + transport_ = transport; + maxLength_ = maxLength; + writeBuffer_.write(sizeFiller_, 0, 4); + } + + public TFramedTransport(TTransport transport) { + transport_ = transport; + maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH; + writeBuffer_.write(sizeFiller_, 0, 4); + } + + public void open() throws TTransportException { + transport_.open(); + } + + public boolean isOpen() { + return transport_.isOpen(); + } + + public void close() { + transport_.close(); + } + + public int read(byte[] buf, int off, int len) throws TTransportException { + int got = readBuffer_.read(buf, off, len); + if (got > 0) { + return got; + } + + // Read another frame of data + readFrame(); + + return readBuffer_.read(buf, off, len); + } + + @Override + public byte[] getBuffer() { + return readBuffer_.getBuffer(); + } + + @Override + public int getBufferPosition() { + return readBuffer_.getBufferPosition(); + } + + @Override + public int getBytesRemainingInBuffer() { + return readBuffer_.getBytesRemainingInBuffer(); + } + + @Override + public void consumeBuffer(int len) { + readBuffer_.consumeBuffer(len); + } + + public void clear() { + readBuffer_.clear(); + } + + private final byte[] i32buf = new byte[4]; + + private void readFrame() throws TTransportException { + transport_.readAll(i32buf, 0, 4); + int size = decodeFrameSize(i32buf); + + if (size < 0) { + close(); + throw new TTransportException(TTransportException.CORRUPTED_DATA, "Read a negative frame size (" + size + ")!"); + } + + if (size > maxLength_) { + close(); + throw new TTransportException(TTransportException.CORRUPTED_DATA, + "Frame size (" + size + ") larger than max length (" + maxLength_ + ")!"); + } + + byte[] buff = new byte[size]; + transport_.readAll(buff, 0, size); + readBuffer_.reset(buff); + } + + public void write(byte[] buf, int off, int len) throws TTransportException { + writeBuffer_.write(buf, off, len); + } + + @Override + public void flush() throws TTransportException { + byte[] buf = writeBuffer_.get(); + int len = writeBuffer_.len() - 4; // account for the prepended frame size + writeBuffer_.reset(); + writeBuffer_.write(sizeFiller_, 0, 4); // make room for the next frame's size data + + encodeFrameSize(len, buf); // this is the frame length without the filler + transport_.write(buf, 0, len + 4); // we have to write the frame size and frame data + transport_.flush(); + } + + public static final void encodeFrameSize(final int frameSize, final byte[] buf) { + buf[0] = (byte)(0xff & (frameSize >> 24)); + buf[1] = (byte)(0xff & (frameSize >> 16)); + buf[2] = (byte)(0xff & (frameSize >> 8)); + buf[3] = (byte)(0xff & (frameSize)); + } + + public static final int decodeFrameSize(final byte[] buf) { + return + ((buf[0] & 0xff) << 24) | + ((buf[1] & 0xff) << 16) | + ((buf[2] & 0xff) << 8) | + ((buf[3] & 0xff)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java new file mode 100644 index 000000000..c3063fe43 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/THttpClient.java @@ -0,0 +1,362 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; + +import java.net.URL; +import java.net.HttpURLConnection; +import java.util.HashMap; +import java.util.Map; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.params.CoreConnectionPNames; + +/** + * HTTP implementation of the TTransport interface. Used for working with a + * Thrift web services implementation (using for example TServlet). + * + * This class offers two implementations of the HTTP transport. + * One uses HttpURLConnection instances, the other HttpClient from Apache + * Http Components. + * The chosen implementation depends on the constructor used to + * create the THttpClient instance. + * Using the THttpClient(String url) constructor or passing null as the + * HttpClient to THttpClient(String url, HttpClient client) will create an + * instance which will use HttpURLConnection. + * + * When using HttpClient, the following configuration leads to 5-15% + * better performance than the HttpURLConnection implementation: + * + * http.protocol.version=HttpVersion.HTTP_1_1 + * http.protocol.content-charset=UTF-8 + * http.protocol.expect-continue=false + * http.connection.stalecheck=false + * + * Also note that under high load, the HttpURLConnection implementation + * may exhaust the open file descriptor limit. + * + * @see <a href="https://issues.apache.org/jira/browse/THRIFT-970">THRIFT-970</a> + */ + +public class THttpClient extends TTransport { + + private URL url_ = null; + + private final ByteArrayOutputStream requestBuffer_ = new ByteArrayOutputStream(); + + private InputStream inputStream_ = null; + + private int connectTimeout_ = 0; + + private int readTimeout_ = 0; + + private Map<String,String> customHeaders_ = null; + + private final HttpHost host; + + private final HttpClient client; + + public static class Factory extends TTransportFactory { + + private final String url; + private final HttpClient client; + + public Factory(String url) { + this.url = url; + this.client = null; + } + + public Factory(String url, HttpClient client) { + this.url = url; + this.client = client; + } + + @Override + public TTransport getTransport(TTransport trans) { + try { + if (null != client) { + return new THttpClient(url, client); + } else { + return new THttpClient(url); + } + } catch (TTransportException tte) { + return null; + } + } + } + + public THttpClient(String url) throws TTransportException { + try { + url_ = new URL(url); + this.client = null; + this.host = null; + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public THttpClient(String url, HttpClient client) throws TTransportException { + try { + url_ = new URL(url); + this.client = client; + this.host = new HttpHost(url_.getHost(), -1 == url_.getPort() ? url_.getDefaultPort() : url_.getPort(), url_.getProtocol()); + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public void setConnectTimeout(int timeout) { + connectTimeout_ = timeout; + if (null != this.client) { + // WARNING, this modifies the HttpClient params, this might have an impact elsewhere if the + // same HttpClient is used for something else. + client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectTimeout_); + } + } + + public void setReadTimeout(int timeout) { + readTimeout_ = timeout; + if (null != this.client) { + // WARNING, this modifies the HttpClient params, this might have an impact elsewhere if the + // same HttpClient is used for something else. + client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeout_); + } + } + + public void setCustomHeaders(Map<String,String> headers) { + customHeaders_ = headers; + } + + public void setCustomHeader(String key, String value) { + if (customHeaders_ == null) { + customHeaders_ = new HashMap<String, String>(); + } + customHeaders_.put(key, value); + } + + public void open() {} + + public void close() { + if (null != inputStream_) { + try { + inputStream_.close(); + } catch (IOException ioe) { + ; + } + inputStream_ = null; + } + } + + public boolean isOpen() { + return true; + } + + public int read(byte[] buf, int off, int len) throws TTransportException { + if (inputStream_ == null) { + throw new TTransportException("Response buffer is empty, no request."); + } + try { + int ret = inputStream_.read(buf, off, len); + if (ret == -1) { + throw new TTransportException("No more data available."); + } + return ret; + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public void write(byte[] buf, int off, int len) { + requestBuffer_.write(buf, off, len); + } + + /** + * copy from org.apache.http.util.EntityUtils#consume. Android has it's own httpcore + * that doesn't have a consume. + */ + private static void consume(final HttpEntity entity) throws IOException { + if (entity == null) { + return; + } + if (entity.isStreaming()) { + InputStream instream = entity.getContent(); + if (instream != null) { + instream.close(); + } + } + } + + private void flushUsingHttpClient() throws TTransportException { + + if (null == this.client) { + throw new TTransportException("Null HttpClient, aborting."); + } + + // Extract request and reset buffer + byte[] data = requestBuffer_.toByteArray(); + requestBuffer_.reset(); + + HttpPost post = null; + + InputStream is = null; + + try { + // Set request to path + query string + post = new HttpPost(this.url_.getFile()); + + // + // Headers are added to the HttpPost instance, not + // to HttpClient. + // + + post.setHeader("Content-Type", "application/x-thrift"); + post.setHeader("Accept", "application/x-thrift"); + post.setHeader("User-Agent", "Java/THttpClient/HC"); + + if (null != customHeaders_) { + for (Map.Entry<String, String> header : customHeaders_.entrySet()) { + post.setHeader(header.getKey(), header.getValue()); + } + } + + post.setEntity(new ByteArrayEntity(data)); + + HttpResponse response = this.client.execute(this.host, post); + int responseCode = response.getStatusLine().getStatusCode(); + + // + // Retrieve the inputstream BEFORE checking the status code so + // resources get freed in the finally clause. + // + + is = response.getEntity().getContent(); + + if (responseCode != HttpStatus.SC_OK) { + throw new TTransportException("HTTP Response code: " + responseCode); + } + + // Read the responses into a byte array so we can release the connection + // early. This implies that the whole content will have to be read in + // memory, and that momentarily we might use up twice the memory (while the + // thrift struct is being read up the chain). + // Proceeding differently might lead to exhaustion of connections and thus + // to app failure. + + byte[] buf = new byte[1024]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + int len = 0; + do { + len = is.read(buf); + if (len > 0) { + baos.write(buf, 0, len); + } + } while (-1 != len); + + try { + // Indicate we're done with the content. + consume(response.getEntity()); + } catch (IOException ioe) { + // We ignore this exception, it might only mean the server has no + // keep-alive capability. + } + + inputStream_ = new ByteArrayInputStream(baos.toByteArray()); + } catch (IOException ioe) { + // Abort method so the connection gets released back to the connection manager + if (null != post) { + post.abort(); + } + throw new TTransportException(ioe); + } finally { + if (null != is) { + // Close the entity's input stream, this will release the underlying connection + try { + is.close(); + } catch (IOException ioe) { + throw new TTransportException(ioe); + } + } + if (post != null) { + post.releaseConnection(); + } + } + } + + public void flush() throws TTransportException { + + if (null != this.client) { + flushUsingHttpClient(); + return; + } + + // Extract request and reset buffer + byte[] data = requestBuffer_.toByteArray(); + requestBuffer_.reset(); + + try { + // Create connection object + HttpURLConnection connection = (HttpURLConnection)url_.openConnection(); + + // Timeouts, only if explicitly set + if (connectTimeout_ > 0) { + connection.setConnectTimeout(connectTimeout_); + } + if (readTimeout_ > 0) { + connection.setReadTimeout(readTimeout_); + } + + // Make the request + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/x-thrift"); + connection.setRequestProperty("Accept", "application/x-thrift"); + connection.setRequestProperty("User-Agent", "Java/THttpClient"); + if (customHeaders_ != null) { + for (Map.Entry<String, String> header : customHeaders_.entrySet()) { + connection.setRequestProperty(header.getKey(), header.getValue()); + } + } + connection.setDoOutput(true); + connection.connect(); + connection.getOutputStream().write(data); + + int responseCode = connection.getResponseCode(); + if (responseCode != HttpURLConnection.HTTP_OK) { + throw new TTransportException("HTTP Response code: " + responseCode); + } + + // Read the responses + inputStream_ = connection.getInputStream(); + + } catch (IOException iox) { + throw new TTransportException(iox); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java new file mode 100644 index 000000000..2d31f392f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TIOStreamTransport.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This is the most commonly used base transport. It takes an InputStream + * and an OutputStream and uses those to perform all transport operations. + * This allows for compatibility with all the nice constructs Java already + * has to provide a variety of types of streams. + * + */ +public class TIOStreamTransport extends TTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TIOStreamTransport.class.getName()); + + /** Underlying inputStream */ + protected InputStream inputStream_ = null; + + /** Underlying outputStream */ + protected OutputStream outputStream_ = null; + + /** + * Subclasses can invoke the default constructor and then assign the input + * streams in the open method. + */ + protected TIOStreamTransport() {} + + /** + * Input stream constructor. + * + * @param is Input stream to read from + */ + public TIOStreamTransport(InputStream is) { + inputStream_ = is; + } + + /** + * Output stream constructor. + * + * @param os Output stream to read from + */ + public TIOStreamTransport(OutputStream os) { + outputStream_ = os; + } + + /** + * Two-way stream constructor. + * + * @param is Input stream to read from + * @param os Output stream to read from + */ + public TIOStreamTransport(InputStream is, OutputStream os) { + inputStream_ = is; + outputStream_ = os; + } + + /** + * + * @return false after close is called. + */ + public boolean isOpen() { + return inputStream_ != null && outputStream_ != null; + } + + /** + * The streams must already be open. This method does nothing. + */ + public void open() throws TTransportException {} + + /** + * Closes both the input and output streams. + */ + public void close() { + if (inputStream_ != null) { + try { + inputStream_.close(); + } catch (IOException iox) { + LOGGER.warn("Error closing input stream.", iox); + } + inputStream_ = null; + } + if (outputStream_ != null) { + try { + outputStream_.close(); + } catch (IOException iox) { + LOGGER.warn("Error closing output stream.", iox); + } + outputStream_ = null; + } + } + + /** + * Reads from the underlying input stream if not null. + */ + public int read(byte[] buf, int off, int len) throws TTransportException { + if (inputStream_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot read from null inputStream"); + } + int bytesRead; + try { + bytesRead = inputStream_.read(buf, off, len); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + if (bytesRead < 0) { + throw new TTransportException(TTransportException.END_OF_FILE, "Socket is closed by peer."); + } + return bytesRead; + } + + /** + * Writes to the underlying output stream if not null. + */ + public void write(byte[] buf, int off, int len) throws TTransportException { + if (outputStream_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream"); + } + try { + outputStream_.write(buf, off, len); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } + + /** + * Flushes the underlying output stream if not null. + */ + public void flush() throws TTransportException { + if (outputStream_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot flush null outputStream"); + } + try { + outputStream_.flush(); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java new file mode 100644 index 000000000..b19ac86d2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryBuffer.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.apache.thrift.TByteArrayOutputStream; +import java.nio.charset.Charset; + +/** + * Memory buffer-based implementation of the TTransport interface. + */ +public class TMemoryBuffer extends TTransport { + /** + * Create a TMemoryBuffer with an initial buffer size of <i>size</i>. The + * internal buffer will grow as necessary to accommodate the size of the data + * being written to it. + * + * @param size the initial size of the buffer + */ + public TMemoryBuffer(int size) { + arr_ = new TByteArrayOutputStream(size); + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + public void open() { + /* Do nothing */ + } + + @Override + public void close() { + /* Do nothing */ + } + + @Override + public int read(byte[] buf, int off, int len) { + byte[] src = arr_.get(); + int amtToRead = (len > arr_.len() - pos_ ? arr_.len() - pos_ : len); + if (amtToRead > 0) { + System.arraycopy(src, pos_, buf, off, amtToRead); + pos_ += amtToRead; + } + return amtToRead; + } + + @Override + public void write(byte[] buf, int off, int len) { + arr_.write(buf, off, len); + } + + /** + * Output the contents of the memory buffer as a String, using the supplied + * encoding + * @param charset the encoding to use + * @return the contents of the memory buffer as a String + */ + public String toString(Charset charset) { + return arr_.toString(charset); + } + + public String inspect() { + StringBuilder buf = new StringBuilder(); + byte[] bytes = arr_.toByteArray(); + for (int i = 0; i < bytes.length; i++) { + buf.append(pos_ == i ? "==>" : "" ).append(Integer.toHexString(bytes[i] & 0xff)).append(" "); + } + return buf.toString(); + } + + // The contents of the buffer + private TByteArrayOutputStream arr_; + + // Position to read next byte from + private int pos_; + + public int length() { + return arr_.size(); + } + + public byte[] getArray() { + return arr_.get(); + } +} + diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java new file mode 100644 index 000000000..2530dcc36 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TMemoryInputTransport.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +public final class TMemoryInputTransport extends TTransport { + + private byte[] buf_; + private int pos_; + private int endPos_; + + public TMemoryInputTransport() { + } + + public TMemoryInputTransport(byte[] buf) { + reset(buf); + } + + public TMemoryInputTransport(byte[] buf, int offset, int length) { + reset(buf, offset, length); + } + + public void reset(byte[] buf) { + reset(buf, 0, buf.length); + } + + public void reset(byte[] buf, int offset, int length) { + buf_ = buf; + pos_ = offset; + endPos_ = offset + length; + } + + public void clear() { + buf_ = null; + } + + @Override + public void close() {} + + @Override + public boolean isOpen() { + return true; + } + + @Override + public void open() throws TTransportException {} + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + int bytesRemaining = getBytesRemainingInBuffer(); + int amtToRead = (len > bytesRemaining ? bytesRemaining : len); + if (amtToRead > 0) { + System.arraycopy(buf_, pos_, buf, off, amtToRead); + consumeBuffer(amtToRead); + } + return amtToRead; + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + throw new UnsupportedOperationException("No writing allowed!"); + } + + @Override + public byte[] getBuffer() { + return buf_; + } + + public int getBufferPosition() { + return pos_; + } + + public int getBytesRemainingInBuffer() { + return endPos_ - pos_; + } + + public void consumeBuffer(int len) { + pos_ += len; + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java new file mode 100644 index 000000000..df37cb06e --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerSocket.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.SocketException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Wrapper around ServerSocketChannel + */ +public class TNonblockingServerSocket extends TNonblockingServerTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingServerSocket.class.getName()); + + /** + * This channel is where all the nonblocking magic happens. + */ + private ServerSocketChannel serverSocketChannel = null; + + /** + * Underlying ServerSocket object + */ + private ServerSocket serverSocket_ = null; + + /** + * Timeout for client sockets from accept + */ + private int clientTimeout_ = 0; + + public static class NonblockingAbstractServerSocketArgs extends + AbstractServerTransportArgs<NonblockingAbstractServerSocketArgs> {} + + /** + * Creates just a port listening server socket + */ + public TNonblockingServerSocket(int port) throws TTransportException { + this(port, 0); + } + + /** + * Creates just a port listening server socket + */ + public TNonblockingServerSocket(int port, int clientTimeout) throws TTransportException { + this(new NonblockingAbstractServerSocketArgs().port(port).clientTimeout(clientTimeout)); + } + + public TNonblockingServerSocket(InetSocketAddress bindAddr) throws TTransportException { + this(bindAddr, 0); + } + + public TNonblockingServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException { + this(new NonblockingAbstractServerSocketArgs().bindAddr(bindAddr).clientTimeout(clientTimeout)); + } + + public TNonblockingServerSocket(NonblockingAbstractServerSocketArgs args) throws TTransportException { + clientTimeout_ = args.clientTimeout; + try { + serverSocketChannel = ServerSocketChannel.open(); + serverSocketChannel.configureBlocking(false); + + // Make server socket + serverSocket_ = serverSocketChannel.socket(); + // Prevent 2MSL delay problem on server restarts + serverSocket_.setReuseAddress(true); + // Bind to listening port + serverSocket_.bind(args.bindAddr, args.backlog); + } catch (IOException ioe) { + serverSocket_ = null; + throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe); + } + } + + public void listen() throws TTransportException { + // Make sure not to block on accept + if (serverSocket_ != null) { + try { + serverSocket_.setSoTimeout(0); + } catch (SocketException sx) { + LOGGER.error("Socket exception while setting socket timeout", sx); + } + } + } + + protected TNonblockingSocket acceptImpl() throws TTransportException { + if (serverSocket_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket."); + } + try { + SocketChannel socketChannel = serverSocketChannel.accept(); + if (socketChannel == null) { + return null; + } + + TNonblockingSocket tsocket = new TNonblockingSocket(socketChannel); + tsocket.setTimeout(clientTimeout_); + return tsocket; + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public void registerSelector(Selector selector) { + try { + // Register the server socket channel, indicating an interest in + // accepting new connections + serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); + } catch (ClosedChannelException e) { + // this shouldn't happen, ideally... + // TODO: decide what to do with this. + } + } + + public void close() { + if (serverSocket_ != null) { + try { + serverSocket_.close(); + } catch (IOException iox) { + LOGGER.warn("WARNING: Could not close server socket: " + iox.getMessage()); + } + serverSocket_ = null; + } + } + + public void interrupt() { + // The thread-safeness of this is dubious, but Java documentation suggests + // that it is safe to do this from a different thread context + close(); + } + + public int getPort() { + if (serverSocket_ == null) + return -1; + return serverSocket_.getLocalPort(); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java new file mode 100644 index 000000000..ba45b09dc --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingServerTransport.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.nio.channels.Selector; + +/** + * Server transport that can be operated in a nonblocking fashion. + */ +public abstract class TNonblockingServerTransport extends TServerTransport { + + public abstract void registerSelector(Selector selector); +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java new file mode 100644 index 000000000..f86a48b42 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingSocket.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transport for use with async client. + */ +public class TNonblockingSocket extends TNonblockingTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TNonblockingSocket.class.getName()); + + /** + * Host and port if passed in, used for lazy non-blocking connect. + */ + private final SocketAddress socketAddress_; + + private final SocketChannel socketChannel_; + + public TNonblockingSocket(String host, int port) throws IOException { + this(host, port, 0); + } + + /** + * Create a new nonblocking socket transport that will be connected to host:port. + * @param host + * @param port + * @throws IOException + */ + public TNonblockingSocket(String host, int port, int timeout) throws IOException { + this(SocketChannel.open(), timeout, new InetSocketAddress(host, port)); + } + + /** + * Constructor that takes an already created socket. + * + * @param socketChannel Already created SocketChannel object + * @throws IOException if there is an error setting up the streams + */ + public TNonblockingSocket(SocketChannel socketChannel) throws IOException { + this(socketChannel, 0, null); + if (!socketChannel.isConnected()) throw new IOException("Socket must already be connected"); + } + + private TNonblockingSocket(SocketChannel socketChannel, int timeout, SocketAddress socketAddress) + throws IOException { + socketChannel_ = socketChannel; + socketAddress_ = socketAddress; + + // make it a nonblocking channel + socketChannel.configureBlocking(false); + + // set options + Socket socket = socketChannel.socket(); + socket.setSoLinger(false, 0); + socket.setTcpNoDelay(true); + socket.setKeepAlive(true); + setTimeout(timeout); + } + + /** + * Register the new SocketChannel with our Selector, indicating + * we'd like to be notified when it's ready for I/O. + * + * @param selector + * @return the selection key for this socket. + */ + public SelectionKey registerSelector(Selector selector, int interests) throws IOException { + return socketChannel_.register(selector, interests); + } + + /** + * Sets the socket timeout, although this implementation never uses blocking operations so it is unused. + * + * @param timeout Milliseconds timeout + */ + public void setTimeout(int timeout) { + try { + socketChannel_.socket().setSoTimeout(timeout); + } catch (SocketException sx) { + LOGGER.warn("Could not set socket timeout.", sx); + } + } + + /** + * Returns a reference to the underlying SocketChannel. + */ + public SocketChannel getSocketChannel() { + return socketChannel_; + } + + /** + * Checks whether the socket is connected. + */ + public boolean isOpen() { + // isConnected() does not return false after close(), but isOpen() does + return socketChannel_.isOpen() && socketChannel_.isConnected(); + } + + /** + * Do not call, the implementation provides its own lazy non-blocking connect. + */ + public void open() throws TTransportException { + throw new RuntimeException("open() is not implemented for TNonblockingSocket"); + } + + /** + * Perform a nonblocking read into buffer. + */ + public int read(ByteBuffer buffer) throws IOException { + return socketChannel_.read(buffer); + } + + + /** + * Reads from the underlying input stream if not null. + */ + public int read(byte[] buf, int off, int len) throws TTransportException { + if ((socketChannel_.validOps() & SelectionKey.OP_READ) != SelectionKey.OP_READ) { + throw new TTransportException(TTransportException.NOT_OPEN, + "Cannot read from write-only socket channel"); + } + try { + return socketChannel_.read(ByteBuffer.wrap(buf, off, len)); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } + + /** + * Perform a nonblocking write of the data in buffer; + */ + public int write(ByteBuffer buffer) throws IOException { + return socketChannel_.write(buffer); + } + + /** + * Writes to the underlying output stream if not null. + */ + public void write(byte[] buf, int off, int len) throws TTransportException { + if ((socketChannel_.validOps() & SelectionKey.OP_WRITE) != SelectionKey.OP_WRITE) { + throw new TTransportException(TTransportException.NOT_OPEN, + "Cannot write to write-only socket channel"); + } + try { + socketChannel_.write(ByteBuffer.wrap(buf, off, len)); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } + + /** + * Noop. + */ + public void flush() throws TTransportException { + // Not supported by SocketChannel. + } + + /** + * Closes the socket. + */ + public void close() { + try { + socketChannel_.close(); + } catch (IOException iox) { + LOGGER.warn("Could not close socket.", iox); + } + } + + /** {@inheritDoc} */ + public boolean startConnect() throws IOException { + return socketChannel_.connect(socketAddress_); + } + + /** {@inheritDoc} */ + public boolean finishConnect() throws IOException { + return socketChannel_.finishConnect(); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java new file mode 100644 index 000000000..43c130688 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TNonblockingTransport.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; + +public abstract class TNonblockingTransport extends TTransport { + + /** + * Non-blocking connection initialization. + * @see java.nio.channels.SocketChannel#connect(SocketAddress remote) + */ + public abstract boolean startConnect() throws IOException; + + /** + * Non-blocking connection completion. + * @see java.nio.channels.SocketChannel#finishConnect() + */ + public abstract boolean finishConnect() throws IOException; + + public abstract SelectionKey registerSelector(Selector selector, int interests) throws IOException; + + public abstract int read(ByteBuffer buffer) throws IOException; + + public abstract int write(ByteBuffer buffer) throws IOException; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java new file mode 100644 index 000000000..73dfaaf18 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java @@ -0,0 +1,447 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.URL; +import java.net.MalformedURLException; +import java.security.KeyStore; +import java.util.Arrays; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Factory for providing and setting up Client and Server SSL wrapped + * TSocket and TServerSocket + */ +public class TSSLTransportFactory { + + private static final Logger LOGGER = + LoggerFactory.getLogger(TSSLTransportFactory.class); + + /** + * Get a SSL wrapped TServerSocket bound to the specified port. In this + * configuration the default settings are used. Default settings are retrieved + * from System properties that are set. + * + * Example system properties: + * -Djavax.net.ssl.trustStore=<truststore location> + * -Djavax.net.ssl.trustStorePassword=password + * -Djavax.net.ssl.keyStore=<keystore location> + * -Djavax.net.ssl.keyStorePassword=password + * + * @param port + * @return A SSL wrapped TServerSocket + * @throws TTransportException + */ + public static TServerSocket getServerSocket(int port) throws TTransportException { + return getServerSocket(port, 0); + } + + /** + * Get a default SSL wrapped TServerSocket bound to the specified port + * + * @param port + * @param clientTimeout + * @return A SSL wrapped TServerSocket + * @throws TTransportException + */ + public static TServerSocket getServerSocket(int port, int clientTimeout) throws TTransportException { + return getServerSocket(port, clientTimeout, false, null); + } + + /** + * Get a default SSL wrapped TServerSocket bound to the specified port and interface + * + * @param port + * @param clientTimeout + * @param ifAddress + * @return A SSL wrapped TServerSocket + * @throws TTransportException + */ + public static TServerSocket getServerSocket(int port, int clientTimeout, boolean clientAuth, InetAddress ifAddress) throws TTransportException { + SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + return createServer(factory, port, clientTimeout, clientAuth, ifAddress, null); + } + + /** + * Get a configured SSL wrapped TServerSocket bound to the specified port and interface. + * Here the TSSLTransportParameters are used to set the values for the algorithms, keystore, + * truststore and other settings + * + * @param port + * @param clientTimeout + * @param ifAddress + * @param params + * @return A SSL wrapped TServerSocket + * @throws TTransportException + */ + public static TServerSocket getServerSocket(int port, int clientTimeout, InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException { + if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) { + throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters"); + } + + SSLContext ctx = createSSLContext(params); + return createServer(ctx.getServerSocketFactory(), port, clientTimeout, params.clientAuth, ifAddress, params); + } + + private static TServerSocket createServer(SSLServerSocketFactory factory, int port, int timeout, boolean clientAuth, + InetAddress ifAddress, TSSLTransportParameters params) throws TTransportException { + try { + SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(port, 100, ifAddress); + serverSocket.setSoTimeout(timeout); + serverSocket.setNeedClientAuth(clientAuth); + if (params != null && params.cipherSuites != null) { + serverSocket.setEnabledCipherSuites(params.cipherSuites); + } + return new TServerSocket(new TServerSocket.ServerSocketTransportArgs(). + serverSocket(serverSocket).clientTimeout(timeout)); + } catch (Exception e) { + throw new TTransportException("Could not bind to port " + port, e); + } + } + + /** + * Get a default SSL wrapped TSocket connected to the specified host and port. All + * the client methods return a bound connection. So there is no need to call open() on the + * TTransport. + * + * @param host + * @param port + * @param timeout + * @return A SSL wrapped TSocket + * @throws TTransportException + */ + public static TSocket getClientSocket(String host, int port, int timeout) throws TTransportException { + SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + return createClient(factory, host, port, timeout); + } + + /** + * Get a default SSL wrapped TSocket connected to the specified host and port. + * + * @param host + * @param port + * @return A SSL wrapped TSocket + * @throws TTransportException + */ + public static TSocket getClientSocket(String host, int port) throws TTransportException { + return getClientSocket(host, port, 0); + } + + /** + * Get a custom configured SSL wrapped TSocket. The SSL settings are obtained from the + * passed in TSSLTransportParameters. + * + * @param host + * @param port + * @param timeout + * @param params + * @return A SSL wrapped TSocket + * @throws TTransportException + */ + public static TSocket getClientSocket(String host, int port, int timeout, TSSLTransportParameters params) throws TTransportException { + if (params == null || !(params.isKeyStoreSet || params.isTrustStoreSet)) { + throw new TTransportException("Either one of the KeyStore or TrustStore must be set for SSLTransportParameters"); + } + + SSLContext ctx = createSSLContext(params); + return createClient(ctx.getSocketFactory(), host, port, timeout); + } + + private static SSLContext createSSLContext(TSSLTransportParameters params) throws TTransportException { + SSLContext ctx; + InputStream in = null; + InputStream is = null; + + try { + ctx = SSLContext.getInstance(params.protocol); + TrustManagerFactory tmf = null; + KeyManagerFactory kmf = null; + + if (params.isTrustStoreSet) { + tmf = TrustManagerFactory.getInstance(params.trustManagerType); + KeyStore ts = KeyStore.getInstance(params.trustStoreType); + if (params.trustStoreStream != null) { + in = params.trustStoreStream; + } else { + in = getStoreAsStream(params.trustStore); + } + ts.load(in, + (params.trustPass != null ? params.trustPass.toCharArray() : null)); + tmf.init(ts); + } + + if (params.isKeyStoreSet) { + kmf = KeyManagerFactory.getInstance(params.keyManagerType); + KeyStore ks = KeyStore.getInstance(params.keyStoreType); + if (params.keyStoreStream != null) { + is = params.keyStoreStream; + } else { + is = getStoreAsStream(params.keyStore); + } + ks.load(is, params.keyPass.toCharArray()); + kmf.init(ks, params.keyPass.toCharArray()); + } + + if (params.isKeyStoreSet && params.isTrustStoreSet) { + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + } + else if (params.isKeyStoreSet) { + ctx.init(kmf.getKeyManagers(), null, null); + } + else { + ctx.init(null, tmf.getTrustManagers(), null); + } + + } catch (Exception e) { + throw new TTransportException("Error creating the transport", e); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + LOGGER.warn("Unable to close stream", e); + } + } + if (is != null) { + try { + is.close(); + } catch (IOException e) { + LOGGER.warn("Unable to close stream", e); + } + } + } + + return ctx; + } + + private static InputStream getStoreAsStream(String store) throws IOException { + try { + return new FileInputStream(store); + } catch(FileNotFoundException e) { + } + + InputStream storeStream = null; + try { + storeStream = new URL(store).openStream(); + if (storeStream != null) { + return storeStream; + } + } catch(MalformedURLException e) { + } + + storeStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(store); + + if (storeStream != null) { + return storeStream; + } else { + throw new IOException("Could not load file: " + store); + } + } + + private static TSocket createClient(SSLSocketFactory factory, String host, int port, int timeout) throws TTransportException { + try { + SSLSocket socket = (SSLSocket) factory.createSocket(host, port); + socket.setSoTimeout(timeout); + return new TSocket(socket); + } catch (Exception e) { + throw new TTransportException("Could not connect to " + host + " on port " + port, e); + } + } + + + /** + * A Class to hold all the SSL parameters + */ + public static class TSSLTransportParameters { + protected String protocol = "TLS"; + protected String keyStore; + protected InputStream keyStoreStream; + protected String keyPass; + protected String keyManagerType = KeyManagerFactory.getDefaultAlgorithm(); + protected String keyStoreType = "JKS"; + protected String trustStore; + protected InputStream trustStoreStream; + protected String trustPass; + protected String trustManagerType = TrustManagerFactory.getDefaultAlgorithm(); + protected String trustStoreType = "JKS"; + protected String[] cipherSuites; + protected boolean clientAuth = false; + protected boolean isKeyStoreSet = false; + protected boolean isTrustStoreSet = false; + + public TSSLTransportParameters() {} + + /** + * Create parameters specifying the protocol and cipher suites + * + * @param protocol The specific protocol (TLS/SSL) can be specified with versions + * @param cipherSuites + */ + public TSSLTransportParameters(String protocol, String[] cipherSuites) { + this(protocol, cipherSuites, false); + } + + /** + * Create parameters specifying the protocol, cipher suites and if client authentication + * is required + * + * @param protocol The specific protocol (TLS/SSL) can be specified with versions + * @param cipherSuites + * @param clientAuth + */ + public TSSLTransportParameters(String protocol, String[] cipherSuites, boolean clientAuth) { + if (protocol != null) { + this.protocol = protocol; + } + this.cipherSuites = cipherSuites != null ? Arrays.copyOf(cipherSuites, cipherSuites.length) : null; + this.clientAuth = clientAuth; + } + + /** + * Set the keystore, password, certificate type and the store type + * + * @param keyStore Location of the Keystore on disk + * @param keyPass Keystore password + * @param keyManagerType The default is X509 + * @param keyStoreType The default is JKS + */ + public void setKeyStore(String keyStore, String keyPass, String keyManagerType, String keyStoreType) { + this.keyStore = keyStore; + this.keyPass = keyPass; + if (keyManagerType != null) { + this.keyManagerType = keyManagerType; + } + if (keyStoreType != null) { + this.keyStoreType = keyStoreType; + } + isKeyStoreSet = true; + } + + /** + * Set the keystore, password, certificate type and the store type + * + * @param keyStoreStream Keystore content input stream + * @param keyPass Keystore password + * @param keyManagerType The default is X509 + * @param keyStoreType The default is JKS + */ + public void setKeyStore(InputStream keyStoreStream, String keyPass, String keyManagerType, String keyStoreType) { + this.keyStoreStream = keyStoreStream; + setKeyStore("", keyPass, keyManagerType, keyStoreType); + } + + /** + * Set the keystore and password + * + * @param keyStore Location of the Keystore on disk + * @param keyPass Keystore password + */ + public void setKeyStore(String keyStore, String keyPass) { + setKeyStore(keyStore, keyPass, null, null); + } + + /** + * Set the keystore and password + * + * @param keyStoreStream Keystore content input stream + * @param keyPass Keystore password + */ + public void setKeyStore(InputStream keyStoreStream, String keyPass) { + setKeyStore(keyStoreStream, keyPass, null, null); + } + + /** + * Set the truststore, password, certificate type and the store type + * + * @param trustStore Location of the Truststore on disk + * @param trustPass Truststore password + * @param trustManagerType The default is X509 + * @param trustStoreType The default is JKS + */ + public void setTrustStore(String trustStore, String trustPass, String trustManagerType, String trustStoreType) { + this.trustStore = trustStore; + this.trustPass = trustPass; + if (trustManagerType != null) { + this.trustManagerType = trustManagerType; + } + if (trustStoreType != null) { + this.trustStoreType = trustStoreType; + } + isTrustStoreSet = true; + } + + /** + * Set the truststore, password, certificate type and the store type + * + * @param trustStoreStream Truststore content input stream + * @param trustPass Truststore password + * @param trustManagerType The default is X509 + * @param trustStoreType The default is JKS + */ + public void setTrustStore(InputStream trustStoreStream, String trustPass, String trustManagerType, String trustStoreType) { + this.trustStoreStream = trustStoreStream; + setTrustStore("", trustPass, trustManagerType, trustStoreType); + } + + /** + * Set the truststore and password + * + * @param trustStore Location of the Truststore on disk + * @param trustPass Truststore password + */ + public void setTrustStore(String trustStore, String trustPass) { + setTrustStore(trustStore, trustPass, null, null); + } + + /** + * Set the truststore and password + * + * @param trustStoreStream Truststore content input stream + * @param trustPass Truststore password + */ + public void setTrustStore(InputStream trustStoreStream, String trustPass) { + setTrustStore(trustStoreStream, trustPass, null, null); + } + + /** + * Set if client authentication is required + * + * @param clientAuth + */ + public void requireClientAuth(boolean clientAuth) { + this.clientAuth = clientAuth; + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java new file mode 100644 index 000000000..4b1ca0a94 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslClientTransport.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Wraps another Thrift <code>TTransport</code>, but performs SASL client + * negotiation on the call to <code>open()</code>. This class will wrap ensuing + * communication over it, if a SASL QOP is negotiated with the other party. + */ +public class TSaslClientTransport extends TSaslTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TSaslClientTransport.class); + + /** + * The name of the mechanism this client supports. + */ + private final String mechanism; + + /** + * Uses the given <code>SaslClient</code>. + * + * @param saslClient + * The <code>SaslClient</code> to use for the subsequent SASL + * negotiation. + * @param transport + * Transport underlying this one. + */ + public TSaslClientTransport(SaslClient saslClient, TTransport transport) { + super(saslClient, transport); + mechanism = saslClient.getMechanismName(); + } + + /** + * Creates a <code>SaslClient</code> using the given SASL-specific parameters. + * See the Java documentation for <code>Sasl.createSaslClient</code> for the + * details of the parameters. + * + * @param transport + * The underlying Thrift transport. + * @throws SaslException + */ + public TSaslClientTransport(String mechanism, String authorizationId, String protocol, + String serverName, Map<String, String> props, CallbackHandler cbh, TTransport transport) + throws SaslException { + super(Sasl.createSaslClient(new String[] { mechanism }, authorizationId, protocol, serverName, + props, cbh), transport); + this.mechanism = mechanism; + } + + + @Override + protected SaslRole getRole() { + return SaslRole.CLIENT; + } + + /** + * Performs the client side of the initial portion of the Thrift SASL + * protocol. Generates and sends the initial response to the server, including + * which mechanism this client wants to use. + */ + @Override + protected void handleSaslStartMessage() throws TTransportException, SaslException { + SaslClient saslClient = getSaslClient(); + + byte[] initialResponse = new byte[0]; + if (saslClient.hasInitialResponse()) + initialResponse = saslClient.evaluateChallenge(initialResponse); + + LOGGER.debug("Sending mechanism name {} and initial response of length {}", mechanism, + initialResponse.length); + + byte[] mechanismBytes = mechanism.getBytes(StandardCharsets.UTF_8); + sendSaslMessage(NegotiationStatus.START, + mechanismBytes); + // Send initial response + sendSaslMessage(saslClient.isComplete() ? NegotiationStatus.COMPLETE : NegotiationStatus.OK, + initialResponse); + underlyingTransport.flush(); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java new file mode 100644 index 000000000..39b81ca43 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslServerTransport.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.lang.ref.WeakReference; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Wraps another Thrift <code>TTransport</code>, but performs SASL server + * negotiation on the call to <code>open()</code>. This class will wrap ensuing + * communication over it, if a SASL QOP is negotiated with the other party. + */ +public class TSaslServerTransport extends TSaslTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TSaslServerTransport.class); + + /** + * Mapping from SASL mechanism name -> all the parameters required to + * instantiate a SASL server. + */ + private Map<String, TSaslServerDefinition> serverDefinitionMap = new HashMap<String, TSaslServerDefinition>(); + + /** + * Contains all the parameters used to define a SASL server implementation. + */ + private static class TSaslServerDefinition { + public String mechanism; + public String protocol; + public String serverName; + public Map<String, String> props; + public CallbackHandler cbh; + + public TSaslServerDefinition(String mechanism, String protocol, String serverName, + Map<String, String> props, CallbackHandler cbh) { + this.mechanism = mechanism; + this.protocol = protocol; + this.serverName = serverName; + this.props = props; + this.cbh = cbh; + } + } + + /** + * Uses the given underlying transport. Assumes that addServerDefinition is + * called later. + * + * @param transport + * Transport underlying this one. + */ + public TSaslServerTransport(TTransport transport) { + super(transport); + } + + /** + * Creates a <code>SaslServer</code> using the given SASL-specific parameters. + * See the Java documentation for <code>Sasl.createSaslServer</code> for the + * details of the parameters. + * + * @param transport + * The underlying Thrift transport. + */ + public TSaslServerTransport(String mechanism, String protocol, String serverName, + Map<String, String> props, CallbackHandler cbh, TTransport transport) { + super(transport); + addServerDefinition(mechanism, protocol, serverName, props, cbh); + } + + private TSaslServerTransport(Map<String, TSaslServerDefinition> serverDefinitionMap, TTransport transport) { + super(transport); + this.serverDefinitionMap.putAll(serverDefinitionMap); + } + + /** + * Add a supported server definition to this transport. See the Java + * documentation for <code>Sasl.createSaslServer</code> for the details of the + * parameters. + */ + public void addServerDefinition(String mechanism, String protocol, String serverName, + Map<String, String> props, CallbackHandler cbh) { + serverDefinitionMap.put(mechanism, new TSaslServerDefinition(mechanism, protocol, serverName, + props, cbh)); + } + + @Override + protected SaslRole getRole() { + return SaslRole.SERVER; + } + + /** + * Performs the server side of the initial portion of the Thrift SASL protocol. + * Receives the initial response from the client, creates a SASL server using + * the mechanism requested by the client (if this server supports it), and + * sends the first challenge back to the client. + */ + @Override + protected void handleSaslStartMessage() throws TTransportException, SaslException { + SaslResponse message = receiveSaslMessage(); + + LOGGER.debug("Received start message with status {}", message.status); + if (message.status != NegotiationStatus.START) { + throw sendAndThrowMessage(NegotiationStatus.ERROR, "Expecting START status, received " + message.status); + } + + // Get the mechanism name. + String mechanismName = new String(message.payload, StandardCharsets.UTF_8); + TSaslServerDefinition serverDefinition = serverDefinitionMap.get(mechanismName); + LOGGER.debug("Received mechanism name '{}'", mechanismName); + + if (serverDefinition == null) { + throw sendAndThrowMessage(NegotiationStatus.BAD, "Unsupported mechanism type " + mechanismName); + } + SaslServer saslServer = Sasl.createSaslServer(serverDefinition.mechanism, + serverDefinition.protocol, serverDefinition.serverName, serverDefinition.props, + serverDefinition.cbh); + setSaslServer(saslServer); + } + + /** + * <code>TTransportFactory</code> to create + * <code>TSaslServerTransports</code>. Ensures that a given + * underlying <code>TTransport</code> instance receives the same + * <code>TSaslServerTransport</code>. This is kind of an awful hack to work + * around the fact that Thrift is designed assuming that + * <code>TTransport</code> instances are stateless, and thus the existing + * <code>TServers</code> use different <code>TTransport</code> instances for + * input and output. + */ + public static class Factory extends TTransportFactory { + + /** + * This is the implementation of the awful hack described above. + * <code>WeakHashMap</code> is used to ensure that we don't leak memory. + */ + private static Map<TTransport, WeakReference<TSaslServerTransport>> transportMap = + Collections.synchronizedMap(new WeakHashMap<TTransport, WeakReference<TSaslServerTransport>>()); + + /** + * Mapping from SASL mechanism name -> all the parameters required to + * instantiate a SASL server. + */ + private Map<String, TSaslServerDefinition> serverDefinitionMap = new HashMap<String, TSaslServerDefinition>(); + + /** + * Create a new Factory. Assumes that <code>addServerDefinition</code> will + * be called later. + */ + public Factory() { + super(); + } + + /** + * Create a new <code>Factory</code>, initially with the single server + * definition given. You may still call <code>addServerDefinition</code> + * later. See the Java documentation for <code>Sasl.createSaslServer</code> + * for the details of the parameters. + */ + public Factory(String mechanism, String protocol, String serverName, + Map<String, String> props, CallbackHandler cbh) { + super(); + addServerDefinition(mechanism, protocol, serverName, props, cbh); + } + + /** + * Add a supported server definition to the transports created by this + * factory. See the Java documentation for + * <code>Sasl.createSaslServer</code> for the details of the parameters. + */ + public void addServerDefinition(String mechanism, String protocol, String serverName, + Map<String, String> props, CallbackHandler cbh) { + serverDefinitionMap.put(mechanism, new TSaslServerDefinition(mechanism, protocol, serverName, + props, cbh)); + } + + /** + * Get a new <code>TSaslServerTransport</code> instance, or reuse the + * existing one if a <code>TSaslServerTransport</code> has already been + * created before using the given <code>TTransport</code> as an underlying + * transport. This ensures that a given underlying transport instance + * receives the same <code>TSaslServerTransport</code>. + */ + @Override + public TTransport getTransport(TTransport base) { + WeakReference<TSaslServerTransport> ret = transportMap.get(base); + if (ret == null || ret.get() == null) { + LOGGER.debug("transport map does not contain key", base); + ret = new WeakReference<TSaslServerTransport>(new TSaslServerTransport(serverDefinitionMap, base)); + try { + ret.get().open(); + } catch (TTransportException e) { + LOGGER.debug("failed to open server transport", e); + throw new RuntimeException(e); + } + transportMap.put(base, ret); // No need for putIfAbsent(). + // Concurrent calls to getTransport() will pass in different TTransports. + } else { + LOGGER.debug("transport map does contain key {}", base); + } + return ret.get(); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java new file mode 100644 index 000000000..4a453b68f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSaslTransport.java @@ -0,0 +1,575 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.thrift.EncodingUtils; +import org.apache.thrift.TByteArrayOutputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A superclass for SASL client/server thrift transports. A subclass need only + * implement the <code>open</code> method. + */ +abstract class TSaslTransport extends TTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TSaslTransport.class); + + protected static final int DEFAULT_MAX_LENGTH = 0x7FFFFFFF; + + protected static final int MECHANISM_NAME_BYTES = 1; + protected static final int STATUS_BYTES = 1; + protected static final int PAYLOAD_LENGTH_BYTES = 4; + + protected static enum SaslRole { + SERVER, CLIENT; + } + + /** + * Status bytes used during the initial Thrift SASL handshake. + */ + protected static enum NegotiationStatus { + START((byte)0x01), + OK((byte)0x02), + BAD((byte)0x03), + ERROR((byte)0x04), + COMPLETE((byte)0x05); + + private final byte value; + + private static final Map<Byte, NegotiationStatus> reverseMap = + new HashMap<Byte, NegotiationStatus>(); + static { + for (NegotiationStatus s : NegotiationStatus.class.getEnumConstants()) { + reverseMap.put(s.getValue(), s); + } + } + + private NegotiationStatus(byte val) { + this.value = val; + } + + public byte getValue() { + return value; + } + + public static NegotiationStatus byValue(byte val) { + return reverseMap.get(val); + } + } + + /** + * Transport underlying this one. + */ + protected TTransport underlyingTransport; + + /** + * Either a SASL client or a SASL server. + */ + private SaslParticipant sasl; + + /** + * Whether or not we should wrap/unwrap reads/writes. Determined by whether or + * not a QOP is negotiated during the SASL handshake. + */ + private boolean shouldWrap = false; + + /** + * Buffer for input. + */ + private TMemoryInputTransport readBuffer = new TMemoryInputTransport(); + + /** + * Buffer for output. + */ + private final TByteArrayOutputStream writeBuffer = new TByteArrayOutputStream(1024); + + /** + * Create a TSaslTransport. It's assumed that setSaslServer will be called + * later to initialize the SASL endpoint underlying this transport. + * + * @param underlyingTransport + * The thrift transport which this transport is wrapping. + */ + protected TSaslTransport(TTransport underlyingTransport) { + this.underlyingTransport = underlyingTransport; + } + + /** + * Create a TSaslTransport which acts as a client. + * + * @param saslClient + * The <code>SaslClient</code> which this transport will use for SASL + * negotiation. + * @param underlyingTransport + * The thrift transport which this transport is wrapping. + */ + protected TSaslTransport(SaslClient saslClient, TTransport underlyingTransport) { + sasl = new SaslParticipant(saslClient); + this.underlyingTransport = underlyingTransport; + } + + protected void setSaslServer(SaslServer saslServer) { + sasl = new SaslParticipant(saslServer); + } + + // Used to read the status byte and payload length. + private final byte[] messageHeader = new byte[STATUS_BYTES + PAYLOAD_LENGTH_BYTES]; + + /** + * Send a complete Thrift SASL message. + * + * @param status + * The status to send. + * @param payload + * The data to send as the payload of this message. + * @throws TTransportException + */ + protected void sendSaslMessage(NegotiationStatus status, byte[] payload) throws TTransportException { + if (payload == null) + payload = new byte[0]; + + messageHeader[0] = status.getValue(); + EncodingUtils.encodeBigEndian(payload.length, messageHeader, STATUS_BYTES); + + LOGGER.debug("{}: Writing message with status {} and payload length {}", + getRole(), status, payload.length); + + underlyingTransport.write(messageHeader); + underlyingTransport.write(payload); + underlyingTransport.flush(); + } + + /** + * Read a complete Thrift SASL message. + * + * @return The SASL status and payload from this message. + * @throws TTransportException + * Thrown if there is a failure reading from the underlying + * transport, or if a status code of BAD or ERROR is encountered. + */ + protected SaslResponse receiveSaslMessage() throws TTransportException { + underlyingTransport.readAll(messageHeader, 0, messageHeader.length); + + byte statusByte = messageHeader[0]; + + NegotiationStatus status = NegotiationStatus.byValue(statusByte); + if (status == null) { + throw sendAndThrowMessage(NegotiationStatus.ERROR, "Invalid status " + statusByte); + } + + int payloadBytes = EncodingUtils.decodeBigEndian(messageHeader, STATUS_BYTES); + if (payloadBytes < 0 || payloadBytes > 104857600 /* 100 MB */) { + throw sendAndThrowMessage( + NegotiationStatus.ERROR, "Invalid payload header length: " + payloadBytes); + } + + byte[] payload = new byte[payloadBytes]; + underlyingTransport.readAll(payload, 0, payload.length); + + if (status == NegotiationStatus.BAD || status == NegotiationStatus.ERROR) { + String remoteMessage = new String(payload, StandardCharsets.UTF_8); + throw new TTransportException("Peer indicated failure: " + remoteMessage); + } + LOGGER.debug("{}: Received message with status {} and payload length {}", + getRole(), status, payload.length); + return new SaslResponse(status, payload); + } + + /** + * Send a Thrift SASL message with the given status (usually BAD or ERROR) and + * string message, and then throw a TTransportException with the given + * message. + * + * @param status + * The Thrift SASL status code to send. Usually BAD or ERROR. + * @param message + * The optional message to send to the other side. + * @throws TTransportException + * Always thrown with the message provided. + * @return always throws TTransportException but declares return type to allow + * throw sendAndThrowMessage(...) to inform compiler control flow + */ + protected TTransportException sendAndThrowMessage(NegotiationStatus status, String message) throws TTransportException { + try { + sendSaslMessage(status, message.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + LOGGER.warn("Could not send failure response", e); + message += "\nAlso, could not send response: " + e.toString(); + } + throw new TTransportException(message); + } + + /** + * Implemented by subclasses to start the Thrift SASL handshake process. When + * this method completes, the <code>SaslParticipant</code> in this class is + * assumed to be initialized. + * + * @throws TTransportException + * @throws SaslException + */ + abstract protected void handleSaslStartMessage() throws TTransportException, SaslException; + + protected abstract SaslRole getRole(); + + /** + * Opens the underlying transport if it's not already open and then performs + * SASL negotiation. If a QOP is negotiated during this SASL handshake, it used + * for all communication on this transport after this call is complete. + */ + @Override + public void open() throws TTransportException { + /* + * readSaslHeader is used to tag whether the SASL header has been read properly. + * If there is a problem in reading the header, there might not be any + * data in the stream, possibly a TCP health check from load balancer. + */ + boolean readSaslHeader = false; + + LOGGER.debug("opening transport {}", this); + if (sasl != null && sasl.isComplete()) + throw new TTransportException("SASL transport already open"); + + if (!underlyingTransport.isOpen()) + underlyingTransport.open(); + + try { + // Negotiate a SASL mechanism. The client also sends its + // initial response, or an empty one. + handleSaslStartMessage(); + readSaslHeader = true; + LOGGER.debug("{}: Start message handled", getRole()); + + SaslResponse message = null; + while (!sasl.isComplete()) { + message = receiveSaslMessage(); + if (message.status != NegotiationStatus.COMPLETE && + message.status != NegotiationStatus.OK) { + throw new TTransportException("Expected COMPLETE or OK, got " + message.status); + } + + byte[] challenge = sasl.evaluateChallengeOrResponse(message.payload); + + // If we are the client, and the server indicates COMPLETE, we don't need to + // send back any further response. + if (message.status == NegotiationStatus.COMPLETE && + getRole() == SaslRole.CLIENT) { + LOGGER.debug("{}: All done!", getRole()); + continue; + } + + sendSaslMessage(sasl.isComplete() ? NegotiationStatus.COMPLETE : NegotiationStatus.OK, + challenge); + } + LOGGER.debug("{}: Main negotiation loop complete", getRole()); + + // If we're the client, and we're complete, but the server isn't + // complete yet, we need to wait for its response. This will occur + // with ANONYMOUS auth, for example, where we send an initial response + // and are immediately complete. + if (getRole() == SaslRole.CLIENT && + (message == null || message.status == NegotiationStatus.OK)) { + LOGGER.debug("{}: SASL Client receiving last message", getRole()); + message = receiveSaslMessage(); + if (message.status != NegotiationStatus.COMPLETE) { + throw new TTransportException( + "Expected SASL COMPLETE, but got " + message.status); + } + } + } catch (SaslException e) { + try { + LOGGER.error("SASL negotiation failure", e); + throw sendAndThrowMessage(NegotiationStatus.BAD, e.getMessage()); + } finally { + underlyingTransport.close(); + } + } catch (TTransportException e) { + // If there is no-data or no-sasl header in the stream, + // log the failure, and clean up the underlying transport. + if (!readSaslHeader && e.getType() == TTransportException.END_OF_FILE) { + underlyingTransport.close(); + LOGGER.debug("No data or no sasl data in the stream during negotiation"); + } + throw e; + } + + String qop = (String) sasl.getNegotiatedProperty(Sasl.QOP); + if (qop != null && !qop.equalsIgnoreCase("auth")) + shouldWrap = true; + } + + /** + * Get the underlying <code>SaslClient</code>. + * + * @return The <code>SaslClient</code>, or <code>null</code> if this transport + * is backed by a <code>SaslServer</code>. + */ + public SaslClient getSaslClient() { + return sasl.saslClient; + } + + /** + * Get the underlying transport that Sasl is using. + * @return The <code>TTransport</code> transport + */ + public TTransport getUnderlyingTransport() { + return underlyingTransport; + } + + /** + * Get the underlying <code>SaslServer</code>. + * + * @return The <code>SaslServer</code>, or <code>null</code> if this transport + * is backed by a <code>SaslClient</code>. + */ + public SaslServer getSaslServer() { + return sasl.saslServer; + } + + /** + * Read a 4-byte word from the underlying transport and interpret it as an + * integer. + * + * @return The length prefix of the next SASL message to read. + * @throws TTransportException + * Thrown if reading from the underlying transport fails. + */ + protected int readLength() throws TTransportException { + byte[] lenBuf = new byte[4]; + underlyingTransport.readAll(lenBuf, 0, lenBuf.length); + return EncodingUtils.decodeBigEndian(lenBuf); + } + + /** + * Write the given integer as 4 bytes to the underlying transport. + * + * @param length + * The length prefix of the next SASL message to write. + * @throws TTransportException + * Thrown if writing to the underlying transport fails. + */ + protected void writeLength(int length) throws TTransportException { + byte[] lenBuf = new byte[4]; + TFramedTransport.encodeFrameSize(length, lenBuf); + underlyingTransport.write(lenBuf); + } + + // Below is the SASL implementation of the TTransport interface. + + /** + * Closes the underlying transport and disposes of the SASL implementation + * underlying this transport. + */ + @Override + public void close() { + underlyingTransport.close(); + try { + sasl.dispose(); + } catch (SaslException e) { + // Not much we can do here. + } + } + + /** + * True if the underlying transport is open and the SASL handshake is + * complete. + */ + @Override + public boolean isOpen() { + return underlyingTransport.isOpen() && sasl != null && sasl.isComplete(); + } + + /** + * Read from the underlying transport. Unwraps the contents if a QOP was + * negotiated during the SASL handshake. + */ + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + if (!isOpen()) + throw new TTransportException("SASL authentication not complete"); + + int got = readBuffer.read(buf, off, len); + if (got > 0) { + return got; + } + + // Read another frame of data + try { + readFrame(); + } catch (SaslException e) { + throw new TTransportException(e); + } catch (TTransportException transportException) { + // If there is no-data or no-sasl header in the stream, log the failure, and rethrow. + if (transportException.getType() == TTransportException.END_OF_FILE) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("No data or no sasl data in the stream during negotiation"); + } + } + throw transportException; + } + + return readBuffer.read(buf, off, len); + } + + /** + * Read a single frame of data from the underlying transport, unwrapping if + * necessary. + * + * @throws TTransportException + * Thrown if there's an error reading from the underlying transport. + * @throws SaslException + * Thrown if there's an error unwrapping the data. + */ + private void readFrame() throws TTransportException, SaslException { + int dataLength = readLength(); + + if (dataLength < 0) + throw new TTransportException("Read a negative frame size (" + dataLength + ")!"); + + byte[] buff = new byte[dataLength]; + LOGGER.debug("{}: reading data length: {}", getRole(), dataLength); + underlyingTransport.readAll(buff, 0, dataLength); + if (shouldWrap) { + buff = sasl.unwrap(buff, 0, buff.length); + LOGGER.debug("data length after unwrap: {}", buff.length); + } + readBuffer.reset(buff); + } + + /** + * Write to the underlying transport. + */ + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + if (!isOpen()) + throw new TTransportException("SASL authentication not complete"); + + writeBuffer.write(buf, off, len); + } + + /** + * Flushes to the underlying transport. Wraps the contents if a QOP was + * negotiated during the SASL handshake. + */ + @Override + public void flush() throws TTransportException { + byte[] buf = writeBuffer.get(); + int dataLength = writeBuffer.len(); + writeBuffer.reset(); + + if (shouldWrap) { + LOGGER.debug("data length before wrap: {}", dataLength); + try { + buf = sasl.wrap(buf, 0, dataLength); + } catch (SaslException e) { + throw new TTransportException(e); + } + dataLength = buf.length; + } + LOGGER.debug("writing data length: {}", dataLength); + writeLength(dataLength); + underlyingTransport.write(buf, 0, dataLength); + underlyingTransport.flush(); + } + + /** + * Used exclusively by readSaslMessage to return both a status and data. + */ + protected static class SaslResponse { + public NegotiationStatus status; + public byte[] payload; + + public SaslResponse(NegotiationStatus status, byte[] payload) { + this.status = status; + this.payload = payload; + } + } + + /** + * Used to abstract over the <code>SaslServer</code> and + * <code>SaslClient</code> classes, which share a lot of their interface, but + * unfortunately don't share a common superclass. + */ + private static class SaslParticipant { + // One of these will always be null. + public SaslServer saslServer; + public SaslClient saslClient; + + public SaslParticipant(SaslServer saslServer) { + this.saslServer = saslServer; + } + + public SaslParticipant(SaslClient saslClient) { + this.saslClient = saslClient; + } + + public byte[] evaluateChallengeOrResponse(byte[] challengeOrResponse) throws SaslException { + if (saslClient != null) { + return saslClient.evaluateChallenge(challengeOrResponse); + } else { + return saslServer.evaluateResponse(challengeOrResponse); + } + } + + public boolean isComplete() { + if (saslClient != null) + return saslClient.isComplete(); + else + return saslServer.isComplete(); + } + + public void dispose() throws SaslException { + if (saslClient != null) + saslClient.dispose(); + else + saslServer.dispose(); + } + + public byte[] unwrap(byte[] buf, int off, int len) throws SaslException { + if (saslClient != null) + return saslClient.unwrap(buf, off, len); + else + return saslServer.unwrap(buf, off, len); + } + + public byte[] wrap(byte[] buf, int off, int len) throws SaslException { + if (saslClient != null) + return saslClient.wrap(buf, off, len); + else + return saslServer.wrap(buf, off, len); + } + + public Object getNegotiatedProperty(String propName) { + if (saslClient != null) + return saslClient.getNegotiatedProperty(propName); + else + return saslServer.getNegotiatedProperty(propName); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java new file mode 100644 index 000000000..e02d36f6c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSeekableFile.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +public interface TSeekableFile { + + public InputStream getInputStream() throws IOException; + public OutputStream getOutputStream() throws IOException; + public void close() throws IOException; + public long length() throws IOException; + public void seek(long pos) throws IOException; +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java new file mode 100644 index 000000000..79f7b7f49 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerSocket.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; + +/** + * Wrapper around ServerSocket for Thrift. + * + */ +public class TServerSocket extends TServerTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TServerSocket.class.getName()); + + /** + * Underlying ServerSocket object + */ + private ServerSocket serverSocket_ = null; + + /** + * Timeout for client sockets from accept + */ + private int clientTimeout_ = 0; + + public static class ServerSocketTransportArgs extends AbstractServerTransportArgs<ServerSocketTransportArgs> { + ServerSocket serverSocket; + + public ServerSocketTransportArgs serverSocket(ServerSocket serverSocket) { + this.serverSocket = serverSocket; + return this; + } + } + + /** + * Creates a server socket from underlying socket object + */ + public TServerSocket(ServerSocket serverSocket) throws TTransportException { + this(serverSocket, 0); + } + + /** + * Creates a server socket from underlying socket object + */ + public TServerSocket(ServerSocket serverSocket, int clientTimeout) throws TTransportException { + this(new ServerSocketTransportArgs().serverSocket(serverSocket).clientTimeout(clientTimeout)); + } + + /** + * Creates just a port listening server socket + */ + public TServerSocket(int port) throws TTransportException { + this(port, 0); + } + + /** + * Creates just a port listening server socket + */ + public TServerSocket(int port, int clientTimeout) throws TTransportException { + this(new InetSocketAddress(port), clientTimeout); + } + + public TServerSocket(InetSocketAddress bindAddr) throws TTransportException { + this(bindAddr, 0); + } + + public TServerSocket(InetSocketAddress bindAddr, int clientTimeout) throws TTransportException { + this(new ServerSocketTransportArgs().bindAddr(bindAddr).clientTimeout(clientTimeout)); + } + + public TServerSocket(ServerSocketTransportArgs args) throws TTransportException { + clientTimeout_ = args.clientTimeout; + if (args.serverSocket != null) { + this.serverSocket_ = args.serverSocket; + return; + } + try { + // Make server socket + serverSocket_ = new ServerSocket(); + // Prevent 2MSL delay problem on server restarts + serverSocket_.setReuseAddress(true); + // Bind to listening port + serverSocket_.bind(args.bindAddr, args.backlog); + } catch (IOException ioe) { + close(); + throw new TTransportException("Could not create ServerSocket on address " + args.bindAddr.toString() + ".", ioe); + } + } + + public void listen() throws TTransportException { + // Make sure to block on accept + if (serverSocket_ != null) { + try { + serverSocket_.setSoTimeout(0); + } catch (SocketException sx) { + LOGGER.error("Could not set socket timeout.", sx); + } + } + } + + protected TSocket acceptImpl() throws TTransportException { + if (serverSocket_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "No underlying server socket."); + } + try { + Socket result = serverSocket_.accept(); + TSocket result2 = new TSocket(result); + result2.setTimeout(clientTimeout_); + return result2; + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public void close() { + if (serverSocket_ != null) { + try { + serverSocket_.close(); + } catch (IOException iox) { + LOGGER.warn("Could not close server socket.", iox); + } + serverSocket_ = null; + } + } + + public void interrupt() { + // The thread-safeness of this is dubious, but Java documentation suggests + // that it is safe to do this from a different thread context + close(); + } + + public ServerSocket getServerSocket() { + return serverSocket_; + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java new file mode 100644 index 000000000..424e4faaa --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TServerTransport.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.Closeable; +import java.net.InetSocketAddress; + +/** + * Server transport. Object which provides client transports. + * + */ +public abstract class TServerTransport implements Closeable { + + public static abstract class AbstractServerTransportArgs<T extends AbstractServerTransportArgs<T>> { + int backlog = 0; // A value of 0 means the default value will be used (currently set at 50) + int clientTimeout = 0; + InetSocketAddress bindAddr; + + public T backlog(int backlog) { + this.backlog = backlog; + return (T) this; + } + + public T clientTimeout(int clientTimeout) { + this.clientTimeout = clientTimeout; + return (T) this; + } + + public T port(int port) { + this.bindAddr = new InetSocketAddress(port); + return (T) this; + } + + public T bindAddr(InetSocketAddress bindAddr) { + this.bindAddr = bindAddr; + return (T) this; + } + } + + public abstract void listen() throws TTransportException; + + public final TTransport accept() throws TTransportException { + TTransport transport = acceptImpl(); + if (transport == null) { + throw new TTransportException("accept() may not return NULL"); + } + return transport; + } + + public abstract void close(); + + protected abstract TTransport acceptImpl() throws TTransportException; + + /** + * Optional method implementation. This signals to the server transport + * that it should break out of any accept() or listen() that it is currently + * blocked on. This method, if implemented, MUST be thread safe, as it may + * be called from a different thread context than the other TServerTransport + * methods. + */ + public void interrupt() {} + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java new file mode 100644 index 000000000..42102d9e8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSimpleFileTransport.java @@ -0,0 +1,216 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.io.RandomAccessFile; + + +/** + * Basic file support for the TTransport interface + */ +public final class TSimpleFileTransport extends TTransport { + + private RandomAccessFile file = null; + private boolean readable; + private boolean writable; + private String path_; + + + /** + * Create a transport backed by a simple file + * + * @param path the path to the file to open/create + * @param read true to support read operations + * @param write true to support write operations + * @param openFile true to open the file on construction + * @throws TTransportException if file open fails + */ + public TSimpleFileTransport(String path, boolean read, + boolean write, boolean openFile) + throws TTransportException { + if (path.length() <= 0) { + throw new TTransportException("No path specified"); + } + if (!read && !write) { + throw new TTransportException("Neither READ nor WRITE specified"); + } + readable = read; + writable = write; + path_ = path; + if (openFile) { + open(); + } + } + + /** + * Create a transport backed by a simple file + * Implicitly opens file to conform to C++ behavior. + * + * @param path the path to the file to open/create + * @param read true to support read operations + * @param write true to support write operations + * @throws TTransportException if file open fails + */ + public TSimpleFileTransport(String path, boolean read, boolean write) + throws TTransportException { + this(path, read, write, true); + } + + /** + * Create a transport backed by a simple read only disk file (implicitly opens + * file) + * + * @param path the path to the file to open/create + * @throws TTransportException if file open fails + */ + public TSimpleFileTransport(String path) throws TTransportException { + this(path, true, false, true); + } + + /** + * Test file status + * + * @return true if open, otherwise false + */ + @Override + public boolean isOpen() { + return (file != null); + } + + /** + * Open file if not previously opened. + * + * @throws TTransportException if open fails + */ + @Override + public void open() throws TTransportException { + if (file == null){ + try { + String access = "r"; //RandomAccessFile objects must be readable + if (writable) { + access += "w"; + } + file = new RandomAccessFile(path_, access); + } catch (IOException ioe) { + file = null; + throw new TTransportException(ioe.getMessage()); + } + } + } + + /** + * Close file, subsequent read/write activity will throw exceptions + */ + @Override + public void close() { + if (file != null) { + try { + file.close(); + } catch (Exception e) { + //Nothing to do + } + file = null; + } + } + + /** + * Read up to len many bytes into buf at offset + * + * @param buf houses bytes read + * @param off offset into buff to begin writing to + * @param len maximum number of bytes to read + * @return number of bytes actually read + * @throws TTransportException on read failure + */ + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + if (!readable) { + throw new TTransportException("Read operation on write only file"); + } + int iBytesRead = 0; + try { + iBytesRead = file.read(buf, off, len); + } catch (IOException ioe) { + file = null; + throw new TTransportException(ioe.getMessage()); + } + return iBytesRead; + } + + /** + * Write len many bytes from buff starting at offset + * + * @param buf buffer containing bytes to write + * @param off offset into buffer to begin writing from + * @param len number of bytes to write + * @throws TTransportException on write failure + */ + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + try { + file.write(buf, off, len); + } catch (IOException ioe) { + file = null; + throw new TTransportException(ioe.getMessage()); + } + } + + /** + * Move file pointer to specified offset, new read/write calls will act here + * + * @param offset bytes from beginning of file to move pointer to + * @throws TTransportException is seek fails + */ + public void seek(long offset) throws TTransportException { + try { + file.seek(offset); + } catch (IOException ex) { + throw new TTransportException(ex.getMessage()); + } + } + + /** + * Return the length of the file in bytes + * + * @return length of the file in bytes + * @throws TTransportException if file access fails + */ + public long length() throws TTransportException { + try { + return file.length(); + } catch (IOException ex) { + throw new TTransportException(ex.getMessage()); + } + } + + /** + * Return current file pointer position in bytes from beginning of file + * + * @return file pointer position + * @throws TTransportException if file access fails + */ + public long getFilePointer() throws TTransportException { + try { + return file.getFilePointer(); + } catch (IOException ex) { + throw new TTransportException(ex.getMessage()); + } + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java new file mode 100644 index 000000000..b20b32b78 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TSocket.java @@ -0,0 +1,248 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; + +/** + * Socket implementation of the TTransport interface. To be commented soon! + * + */ +public class TSocket extends TIOStreamTransport { + + private static final Logger LOGGER = LoggerFactory.getLogger(TSocket.class.getName()); + + /** + * Wrapped Socket object + */ + private Socket socket_; + + /** + * Remote host + */ + private String host_; + + /** + * Remote port + */ + private int port_; + + /** + * Socket timeout - read timeout on the socket + */ + private int socketTimeout_; + + /** + * Connection timeout + */ + private int connectTimeout_; + + /** + * Constructor that takes an already created socket. + * + * @param socket Already created socket object + * @throws TTransportException if there is an error setting up the streams + */ + public TSocket(Socket socket) throws TTransportException { + socket_ = socket; + try { + socket_.setSoLinger(false, 0); + socket_.setTcpNoDelay(true); + socket_.setKeepAlive(true); + } catch (SocketException sx) { + LOGGER.warn("Could not configure socket.", sx); + } + + if (isOpen()) { + try { + inputStream_ = new BufferedInputStream(socket_.getInputStream()); + outputStream_ = new BufferedOutputStream(socket_.getOutputStream()); + } catch (IOException iox) { + close(); + throw new TTransportException(TTransportException.NOT_OPEN, iox); + } + } + } + + /** + * Creates a new unconnected socket that will connect to the given host + * on the given port. + * + * @param host Remote host + * @param port Remote port + */ + public TSocket(String host, int port) { + this(host, port, 0); + } + + /** + * Creates a new unconnected socket that will connect to the given host + * on the given port. + * + * @param host Remote host + * @param port Remote port + * @param timeout Socket timeout and connection timeout + */ + public TSocket(String host, int port, int timeout) { + this(host, port, timeout, timeout); + } + + /** + * Creates a new unconnected socket that will connect to the given host + * on the given port, with a specific connection timeout and a + * specific socket timeout. + * + * @param host Remote host + * @param port Remote port + * @param socketTimeout Socket timeout + * @param connectTimeout Connection timeout + */ + public TSocket(String host, int port, int socketTimeout, int connectTimeout) { + host_ = host; + port_ = port; + socketTimeout_ = socketTimeout; + connectTimeout_ = connectTimeout; + initSocket(); + } + + /** + * Initializes the socket object + */ + private void initSocket() { + socket_ = new Socket(); + try { + socket_.setSoLinger(false, 0); + socket_.setTcpNoDelay(true); + socket_.setKeepAlive(true); + socket_.setSoTimeout(socketTimeout_); + } catch (SocketException sx) { + LOGGER.error("Could not configure socket.", sx); + } + } + + /** + * Sets the socket timeout and connection timeout. + * + * @param timeout Milliseconds timeout + */ + public void setTimeout(int timeout) { + this.setConnectTimeout(timeout); + this.setSocketTimeout(timeout); + } + + /** + * Sets the time after which the connection attempt will time out + * + * @param timeout Milliseconds timeout + */ + public void setConnectTimeout(int timeout) { + connectTimeout_ = timeout; + } + + /** + * Sets the socket timeout + * + * @param timeout Milliseconds timeout + */ + public void setSocketTimeout(int timeout) { + socketTimeout_ = timeout; + try { + socket_.setSoTimeout(timeout); + } catch (SocketException sx) { + LOGGER.warn("Could not set socket timeout.", sx); + } + } + + /** + * Returns a reference to the underlying socket. + */ + public Socket getSocket() { + if (socket_ == null) { + initSocket(); + } + return socket_; + } + + /** + * Checks whether the socket is connected. + */ + public boolean isOpen() { + if (socket_ == null) { + return false; + } + return socket_.isConnected(); + } + + /** + * Connects the socket, creating a new socket object if necessary. + */ + public void open() throws TTransportException { + if (isOpen()) { + throw new TTransportException(TTransportException.ALREADY_OPEN, "Socket already connected."); + } + + if (host_ == null || host_.length() == 0) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open null host."); + } + if (port_ <= 0 || port_ > 65535) { + throw new TTransportException(TTransportException.NOT_OPEN, "Invalid port " + port_); + } + + if (socket_ == null) { + initSocket(); + } + + try { + socket_.connect(new InetSocketAddress(host_, port_), connectTimeout_); + inputStream_ = new BufferedInputStream(socket_.getInputStream()); + outputStream_ = new BufferedOutputStream(socket_.getOutputStream()); + } catch (IOException iox) { + close(); + throw new TTransportException(TTransportException.NOT_OPEN, iox); + } + } + + /** + * Closes the socket. + */ + public void close() { + // Close the underlying streams + super.close(); + + // Close the socket + if (socket_ != null) { + try { + socket_.close(); + } catch (IOException iox) { + LOGGER.warn("Could not close socket.", iox); + } + socket_ = null; + } + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java new file mode 100644 index 000000000..7a33af8ee --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TStandardFile.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +public class TStandardFile implements TSeekableFile { + + protected String path_ = null; + protected RandomAccessFile inputFile_ = null; + + public TStandardFile(String path) throws IOException { + path_ = path; + inputFile_ = new RandomAccessFile(path_, "r"); + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(inputFile_.getFD()); + } + + public OutputStream getOutputStream() throws IOException { + return new FileOutputStream(path_); + } + + public void close() throws IOException { + if(inputFile_ != null) { + inputFile_.close(); + } + } + + public long length() throws IOException { + return inputFile_.length(); + } + + public void seek(long pos) throws IOException { + inputFile_.seek(pos); + } +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java new file mode 100644 index 000000000..73ad730ce --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransport.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.Closeable; + +/** + * Generic class that encapsulates the I/O layer. This is basically a thin + * wrapper around the combined functionality of Java input/output streams. + * + */ +public abstract class TTransport implements Closeable { + + /** + * Queries whether the transport is open. + * + * @return True if the transport is open. + */ + public abstract boolean isOpen(); + + /** + * Is there more data to be read? + * + * @return True if the remote side is still alive and feeding us + */ + public boolean peek() { + return isOpen(); + } + + /** + * Opens the transport for reading/writing. + * + * @throws TTransportException if the transport could not be opened + */ + public abstract void open() + throws TTransportException; + + /** + * Closes the transport. + */ + public abstract void close(); + + /** + * Reads up to len bytes into buffer buf, starting at offset off. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read + * @throws TTransportException if there was an error reading data + */ + public abstract int read(byte[] buf, int off, int len) + throws TTransportException; + + /** + * Guarantees that all of len bytes are actually read off the transport. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read, which must be equal to len + * @throws TTransportException if there was an error reading data + */ + public int readAll(byte[] buf, int off, int len) + throws TTransportException { + int got = 0; + int ret = 0; + while (got < len) { + ret = read(buf, off+got, len-got); + if (ret <= 0) { + throw new TTransportException( + "Cannot read. Remote side has closed. Tried to read " + + len + + " bytes, but only got " + + got + + " bytes. (This is often indicative of an internal error on the server side. Please check your server logs.)"); + } + got += ret; + } + return got; + } + + /** + * Writes the buffer to the output + * + * @param buf The output data buffer + * @throws TTransportException if an error occurs writing data + */ + public void write(byte[] buf) throws TTransportException { + write(buf, 0, buf.length); + } + + /** + * Writes up to len bytes from the buffer. + * + * @param buf The output data buffer + * @param off The offset to start writing from + * @param len The number of bytes to write + * @throws TTransportException if there was an error writing data + */ + public abstract void write(byte[] buf, int off, int len) + throws TTransportException; + + /** + * Flush any pending data out of a transport buffer. + * + * @throws TTransportException if there was an error writing out data. + */ + public void flush() + throws TTransportException {} + + /** + * Access the protocol's underlying buffer directly. If this is not a + * buffered transport, return null. + * @return protocol's Underlying buffer + */ + public byte[] getBuffer() { + return null; + } + + /** + * Return the index within the underlying buffer that specifies the next spot + * that should be read from. + * @return index within the underlying buffer that specifies the next spot + * that should be read from + */ + public int getBufferPosition() { + return 0; + } + + /** + * Get the number of bytes remaining in the underlying buffer. Returns -1 if + * this is a non-buffered transport. + * @return the number of bytes remaining in the underlying buffer. <br> Returns -1 if + * this is a non-buffered transport. + */ + public int getBytesRemainingInBuffer() { + return -1; + } + + /** + * Consume len bytes from the underlying buffer. + * @param len + */ + public void consumeBuffer(int len) {} +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java new file mode 100644 index 000000000..b886bc269 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportException.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.apache.thrift.TException; + +/** + * Transport exceptions. + * + */ +public class TTransportException extends TException { + + private static final long serialVersionUID = 1L; + + public static final int UNKNOWN = 0; + public static final int NOT_OPEN = 1; + public static final int ALREADY_OPEN = 2; + public static final int TIMED_OUT = 3; + public static final int END_OF_FILE = 4; + public static final int CORRUPTED_DATA = 5; + + protected int type_ = UNKNOWN; + + public TTransportException() { + super(); + } + + public TTransportException(int type) { + super(); + type_ = type; + } + + public TTransportException(int type, String message) { + super(message); + type_ = type; + } + + public TTransportException(String message) { + super(message); + } + + public TTransportException(int type, Throwable cause) { + super(cause); + type_ = type; + } + + public TTransportException(Throwable cause) { + super(cause); + } + + public TTransportException(String message, Throwable cause) { + super(message, cause); + } + + public TTransportException(int type, String message, Throwable cause) { + super(message, cause); + type_ = type; + } + + public int getType() { + return type_; + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java new file mode 100644 index 000000000..3e71630ae --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TTransportFactory.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +/** + * Factory class used to create wrapped instance of Transports. + * This is used primarily in servers, which get Transports from + * a ServerTransport and then may want to mutate them (i.e. create + * a BufferedTransport from the underlying base transport) + * + */ +public class TTransportFactory { + + /** + * Return a wrapped instance of the base Transport. + * + * @param trans The base transport + * @return Wrapped Transport + */ + public TTransport getTransport(TTransport trans) { + return trans; + } + +} diff --git a/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java new file mode 100644 index 000000000..e755aa532 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/transport/TZlibTransport.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; + +/** + * TZlibTransport deflates on write and inflates on read. + */ +public class TZlibTransport extends TIOStreamTransport { + + private TTransport transport_ = null; + + public static class Factory extends TTransportFactory { + public Factory() { + } + + @Override + public TTransport getTransport(TTransport base) { + return new TZlibTransport(base); + } + } + + /** + * Constructs a new TZlibTransport instance. + * @param transport the underlying transport to read from and write to + */ + public TZlibTransport(TTransport transport) { + this(transport, Deflater.BEST_COMPRESSION); + } + + /** + * Constructs a new TZlibTransport instance. + * @param transport the underlying transport to read from and write to + * @param compressionLevel 0 for no compression, 9 for maximum compression + */ + public TZlibTransport(TTransport transport, int compressionLevel) { + transport_ = transport; + inputStream_ = new InflaterInputStream(new TTransportInputStream(transport_), new Inflater()); + outputStream_ = new DeflaterOutputStream(new TTransportOutputStream(transport_), new Deflater(compressionLevel, false), true); + } + + @Override + public boolean isOpen() { + return transport_.isOpen(); + } + + @Override + public void open() throws TTransportException { + transport_.open(); + } + + @Override + public void close() { + super.close(); + if (transport_.isOpen()) { + transport_.close(); + } + } +} + +class TTransportInputStream extends InputStream { + + private TTransport transport = null; + + public TTransportInputStream(TTransport transport) { + this.transport = transport; + } + + @Override + public int read() throws IOException { + try { + byte[] buf = new byte[1]; + transport.read(buf, 0, 1); + return buf[0]; + } catch (TTransportException e) { + throw new IOException(e); + } + } + + @Override + public int read(byte b[], int off, int len) throws IOException { + try { + return transport.read(b, off, len); + } catch (TTransportException e) { + throw new IOException(e); + } + } +} + +class TTransportOutputStream extends OutputStream { + + private TTransport transport = null; + + public TTransportOutputStream(TTransport transport) { + this.transport = transport; + } + + @Override + public void write(final int b) throws IOException { + try { + transport.write(new byte[]{(byte) b}); + } catch (TTransportException e) { + throw new IOException(e); + } + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + try { + transport.write(b, off, len); + } catch (TTransportException e) { + throw new IOException(e); + } + } + + @Override + public void flush() throws IOException { + try { + transport.flush(); + } catch (TTransportException e) { + throw new IOException(e); + } + } +} + diff --git a/src/jaegertracing/thrift/lib/java/test/.keystore b/src/jaegertracing/thrift/lib/java/test/.keystore Binary files differnew file mode 100644 index 000000000..4dd66ac07 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/.keystore diff --git a/src/jaegertracing/thrift/lib/java/test/.truststore b/src/jaegertracing/thrift/lib/java/test/.truststore Binary files differnew file mode 100644 index 000000000..26fbd195f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/.truststore diff --git a/src/jaegertracing/thrift/lib/java/test/log4j.properties b/src/jaegertracing/thrift/lib/java/test/log4j.properties new file mode 100644 index 000000000..ab9bebafe --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/log4j.properties @@ -0,0 +1,6 @@ +# log4j configuration used during build and unit tests +log4j.rootLogger=debug,stdout +log4j.threshold=ALL +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} %-5p %c{2} (%F:%M(%L)) - %m%n diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java new file mode 100644 index 000000000..61f40a590 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/Fixtures.java @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import thrift.test.Bonk; +import thrift.test.CompactProtoTestStruct; +import thrift.test.HolyMoley; +import thrift.test.Nesting; +import thrift.test.OneOfEach; + +public class Fixtures { + public static final OneOfEach oneOfEach; + public static final Nesting nesting; + public static final HolyMoley holyMoley; + public static final CompactProtoTestStruct compactProtoTestStruct; + + // These byte arrays are serialized versions of the above structs. + // They were serialized in binary protocol using thrift 0.6.x and are used to + // test backwards compatibility with respect to the standard scheme. + public static final byte[] persistentBytesOneOfEach = new byte[] { (byte) 0x02, (byte) 0x00, + (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x02, + (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x03, (byte) 0xD6, + (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x69, (byte) 0x78, + (byte) 0x08, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x06, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x65, + (byte) 0xA0, (byte) 0xBC, (byte) 0x00, (byte) 0x04, (byte) 0x00, + (byte) 0x07, (byte) 0x40, (byte) 0x09, (byte) 0x21, (byte) 0xFB, + (byte) 0x54, (byte) 0x44, (byte) 0x2D, (byte) 0x18, (byte) 0x0B, + (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x0D, (byte) 0x4A, (byte) 0x53, (byte) 0x4F, (byte) 0x4E, + (byte) 0x20, (byte) 0x54, (byte) 0x48, (byte) 0x49, (byte) 0x53, + (byte) 0x21, (byte) 0x20, (byte) 0x22, (byte) 0x01, (byte) 0x0B, + (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x2E, (byte) 0xD3, (byte) 0x80, (byte) 0xE2, (byte) 0x85, + (byte) 0xAE, (byte) 0xCE, (byte) 0x9D, (byte) 0x20, (byte) 0xD0, + (byte) 0x9D, (byte) 0xCE, (byte) 0xBF, (byte) 0xE2, (byte) 0x85, + (byte) 0xBF, (byte) 0xD0, (byte) 0xBE, (byte) 0xC9, (byte) 0xA1, + (byte) 0xD0, (byte) 0xB3, (byte) 0xD0, (byte) 0xB0, (byte) 0xCF, + (byte) 0x81, (byte) 0xE2, (byte) 0x84, (byte) 0x8E, (byte) 0x20, + (byte) 0xCE, (byte) 0x91, (byte) 0x74, (byte) 0x74, (byte) 0xCE, + (byte) 0xB1, (byte) 0xE2, (byte) 0x85, (byte) 0xBD, (byte) 0xCE, + (byte) 0xBA, (byte) 0xEF, (byte) 0xBF, (byte) 0xBD, (byte) 0xE2, + (byte) 0x80, (byte) 0xBC, (byte) 0x02, (byte) 0x00, (byte) 0x0A, + (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x0B, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x62, (byte) 0x61, + (byte) 0x73, (byte) 0x65, (byte) 0x36, (byte) 0x34, (byte) 0x0F, + (byte) 0x00, (byte) 0x0C, (byte) 0x03, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x02, (byte) 0x03, + (byte) 0x0F, (byte) 0x00, (byte) 0x0D, (byte) 0x06, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x0F, + (byte) 0x00, (byte) 0x0E, (byte) 0x0A, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x03, (byte) 0x00 }; + public static final byte[] persistentBytesNesting = new byte[] { (byte) 0x0C, (byte) 0x00, + (byte) 0x01, (byte) 0x08, (byte) 0x00, (byte) 0x01, (byte) 0x00, + (byte) 0x00, (byte) 0x7A, (byte) 0x69, (byte) 0x0B, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x13, + (byte) 0x49, (byte) 0x20, (byte) 0x61, (byte) 0x6D, (byte) 0x20, + (byte) 0x61, (byte) 0x20, (byte) 0x62, (byte) 0x6F, (byte) 0x6E, + (byte) 0x6B, (byte) 0x2E, (byte) 0x2E, (byte) 0x2E, (byte) 0x20, + (byte) 0x78, (byte) 0x6F, (byte) 0x72, (byte) 0x21, (byte) 0x00, + (byte) 0x0C, (byte) 0x00, (byte) 0x02, (byte) 0x02, (byte) 0x00, + (byte) 0x01, (byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x02, + (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x03, (byte) 0xD6, + (byte) 0x06, (byte) 0x00, (byte) 0x04, (byte) 0x69, (byte) 0x78, + (byte) 0x08, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x06, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x65, + (byte) 0xA0, (byte) 0xBC, (byte) 0x00, (byte) 0x04, (byte) 0x00, + (byte) 0x07, (byte) 0x40, (byte) 0x09, (byte) 0x21, (byte) 0xFB, + (byte) 0x54, (byte) 0x44, (byte) 0x2D, (byte) 0x18, (byte) 0x0B, + (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x0D, (byte) 0x4A, (byte) 0x53, (byte) 0x4F, (byte) 0x4E, + (byte) 0x20, (byte) 0x54, (byte) 0x48, (byte) 0x49, (byte) 0x53, + (byte) 0x21, (byte) 0x20, (byte) 0x22, (byte) 0x01, (byte) 0x0B, + (byte) 0x00, (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x2E, (byte) 0xD3, (byte) 0x80, (byte) 0xE2, (byte) 0x85, + (byte) 0xAE, (byte) 0xCE, (byte) 0x9D, (byte) 0x20, (byte) 0xD0, + (byte) 0x9D, (byte) 0xCE, (byte) 0xBF, (byte) 0xE2, (byte) 0x85, + (byte) 0xBF, (byte) 0xD0, (byte) 0xBE, (byte) 0xC9, (byte) 0xA1, + (byte) 0xD0, (byte) 0xB3, (byte) 0xD0, (byte) 0xB0, (byte) 0xCF, + (byte) 0x81, (byte) 0xE2, (byte) 0x84, (byte) 0x8E, (byte) 0x20, + (byte) 0xCE, (byte) 0x91, (byte) 0x74, (byte) 0x74, (byte) 0xCE, + (byte) 0xB1, (byte) 0xE2, (byte) 0x85, (byte) 0xBD, (byte) 0xCE, + (byte) 0xBA, (byte) 0xEF, (byte) 0xBF, (byte) 0xBD, (byte) 0xE2, + (byte) 0x80, (byte) 0xBC, (byte) 0x02, (byte) 0x00, (byte) 0x0A, + (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x0B, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x06, (byte) 0x62, (byte) 0x61, + (byte) 0x73, (byte) 0x65, (byte) 0x36, (byte) 0x34, (byte) 0x0F, + (byte) 0x00, (byte) 0x0C, (byte) 0x03, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0x02, (byte) 0x03, + (byte) 0x0F, (byte) 0x00, (byte) 0x0D, (byte) 0x06, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x0F, + (byte) 0x00, (byte) 0x0E, (byte) 0x0A, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x03, (byte) 0x00, (byte) 0x00 }; + public static final byte[] persistentBytesHolyMoley = new byte[] { (byte) 0x0F, (byte) 0x00, + (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x01, + (byte) 0x02, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, + (byte) 0x00, (byte) 0x03, (byte) 0x23, (byte) 0x06, (byte) 0x00, + (byte) 0x04, (byte) 0x69, (byte) 0x78, (byte) 0x08, (byte) 0x00, + (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x0A, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x65, (byte) 0xA0, (byte) 0xBC, + (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0x40, + (byte) 0x09, (byte) 0x21, (byte) 0xFB, (byte) 0x54, (byte) 0x44, + (byte) 0x2D, (byte) 0x18, (byte) 0x0B, (byte) 0x00, (byte) 0x08, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0D, (byte) 0x4A, + (byte) 0x53, (byte) 0x4F, (byte) 0x4E, (byte) 0x20, (byte) 0x54, + (byte) 0x48, (byte) 0x49, (byte) 0x53, (byte) 0x21, (byte) 0x20, + (byte) 0x22, (byte) 0x01, (byte) 0x0B, (byte) 0x00, (byte) 0x09, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x2E, (byte) 0xD3, + (byte) 0x80, (byte) 0xE2, (byte) 0x85, (byte) 0xAE, (byte) 0xCE, + (byte) 0x9D, (byte) 0x20, (byte) 0xD0, (byte) 0x9D, (byte) 0xCE, + (byte) 0xBF, (byte) 0xE2, (byte) 0x85, (byte) 0xBF, (byte) 0xD0, + (byte) 0xBE, (byte) 0xC9, (byte) 0xA1, (byte) 0xD0, (byte) 0xB3, + (byte) 0xD0, (byte) 0xB0, (byte) 0xCF, (byte) 0x81, (byte) 0xE2, + (byte) 0x84, (byte) 0x8E, (byte) 0x20, (byte) 0xCE, (byte) 0x91, + (byte) 0x74, (byte) 0x74, (byte) 0xCE, (byte) 0xB1, (byte) 0xE2, + (byte) 0x85, (byte) 0xBD, (byte) 0xCE, (byte) 0xBA, (byte) 0xEF, + (byte) 0xBF, (byte) 0xBD, (byte) 0xE2, (byte) 0x80, (byte) 0xBC, + (byte) 0x02, (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x0B, + (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x06, (byte) 0x62, (byte) 0x61, (byte) 0x73, (byte) 0x65, + (byte) 0x36, (byte) 0x34, (byte) 0x0F, (byte) 0x00, (byte) 0x0C, + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, + (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x0F, (byte) 0x00, + (byte) 0x0D, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, + (byte) 0x00, (byte) 0x03, (byte) 0x0F, (byte) 0x00, (byte) 0x0E, + (byte) 0x0A, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x02, + (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x03, (byte) 0x00, + (byte) 0x03, (byte) 0xD6, (byte) 0x06, (byte) 0x00, (byte) 0x04, + (byte) 0x69, (byte) 0x78, (byte) 0x08, (byte) 0x00, (byte) 0x05, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0A, + (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x65, (byte) 0xA0, (byte) 0xBC, (byte) 0x00, + (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0x40, (byte) 0x09, + (byte) 0x21, (byte) 0xFB, (byte) 0x54, (byte) 0x44, (byte) 0x2D, + (byte) 0x18, (byte) 0x0B, (byte) 0x00, (byte) 0x08, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x0D, (byte) 0x4A, (byte) 0x53, + (byte) 0x4F, (byte) 0x4E, (byte) 0x20, (byte) 0x54, (byte) 0x48, + (byte) 0x49, (byte) 0x53, (byte) 0x21, (byte) 0x20, (byte) 0x22, + (byte) 0x01, (byte) 0x0B, (byte) 0x00, (byte) 0x09, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x2E, (byte) 0xD3, (byte) 0x80, + (byte) 0xE2, (byte) 0x85, (byte) 0xAE, (byte) 0xCE, (byte) 0x9D, + (byte) 0x20, (byte) 0xD0, (byte) 0x9D, (byte) 0xCE, (byte) 0xBF, + (byte) 0xE2, (byte) 0x85, (byte) 0xBF, (byte) 0xD0, (byte) 0xBE, + (byte) 0xC9, (byte) 0xA1, (byte) 0xD0, (byte) 0xB3, (byte) 0xD0, + (byte) 0xB0, (byte) 0xCF, (byte) 0x81, (byte) 0xE2, (byte) 0x84, + (byte) 0x8E, (byte) 0x20, (byte) 0xCE, (byte) 0x91, (byte) 0x74, + (byte) 0x74, (byte) 0xCE, (byte) 0xB1, (byte) 0xE2, (byte) 0x85, + (byte) 0xBD, (byte) 0xCE, (byte) 0xBA, (byte) 0xEF, (byte) 0xBF, + (byte) 0xBD, (byte) 0xE2, (byte) 0x80, (byte) 0xBC, (byte) 0x02, + (byte) 0x00, (byte) 0x0A, (byte) 0x00, (byte) 0x0B, (byte) 0x00, + (byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, + (byte) 0x62, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x36, + (byte) 0x34, (byte) 0x0F, (byte) 0x00, (byte) 0x0C, (byte) 0x03, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, + (byte) 0x02, (byte) 0x03, (byte) 0x0F, (byte) 0x00, (byte) 0x0D, + (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x00, + (byte) 0x03, (byte) 0x0F, (byte) 0x00, (byte) 0x0E, (byte) 0x0A, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x0E, + (byte) 0x00, (byte) 0x02, (byte) 0x0F, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x03, (byte) 0x0B, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x0B, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x0F, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x6E, + (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x6F, (byte) 0x6E, + (byte) 0x65, (byte) 0x2C, (byte) 0x20, (byte) 0x74, (byte) 0x77, + (byte) 0x6F, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, + (byte) 0x74, (byte) 0x68, (byte) 0x72, (byte) 0x65, (byte) 0x65, + (byte) 0x21, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06, + (byte) 0x46, (byte) 0x4F, (byte) 0x55, (byte) 0x52, (byte) 0x21, + (byte) 0x21, (byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x09, + (byte) 0x61, (byte) 0x6E, (byte) 0x64, (byte) 0x20, (byte) 0x61, + (byte) 0x20, (byte) 0x6F, (byte) 0x6E, (byte) 0x65, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x09, (byte) 0x61, (byte) 0x6E, + (byte) 0x64, (byte) 0x20, (byte) 0x61, (byte) 0x20, (byte) 0x74, + (byte) 0x77, (byte) 0x6F, (byte) 0x0D, (byte) 0x00, (byte) 0x03, + (byte) 0x0B, (byte) 0x0F, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x03, + (byte) 0x74, (byte) 0x77, (byte) 0x6F, (byte) 0x0C, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x08, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, + (byte) 0x0B, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x05, (byte) 0x57, (byte) 0x61, (byte) 0x69, + (byte) 0x74, (byte) 0x2E, (byte) 0x00, (byte) 0x08, (byte) 0x00, + (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x0B, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x05, (byte) 0x57, (byte) 0x68, (byte) 0x61, + (byte) 0x74, (byte) 0x3F, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x05, (byte) 0x74, (byte) 0x68, (byte) 0x72, + (byte) 0x65, (byte) 0x65, (byte) 0x0C, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x04, (byte) 0x7A, (byte) 0x65, (byte) 0x72, (byte) 0x6F, + (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00 }; + + private static final byte[] kUnicodeBytes = { (byte) 0xd3, (byte) 0x80, + (byte) 0xe2, (byte) 0x85, (byte) 0xae, (byte) 0xce, (byte) 0x9d, + (byte) 0x20, (byte) 0xd0, (byte) 0x9d, (byte) 0xce, (byte) 0xbf, + (byte) 0xe2, (byte) 0x85, (byte) 0xbf, (byte) 0xd0, (byte) 0xbe, + (byte) 0xc9, (byte) 0xa1, (byte) 0xd0, (byte) 0xb3, (byte) 0xd0, + (byte) 0xb0, (byte) 0xcf, (byte) 0x81, (byte) 0xe2, (byte) 0x84, + (byte) 0x8e, (byte) 0x20, (byte) 0xce, (byte) 0x91, (byte) 0x74, + (byte) 0x74, (byte) 0xce, (byte) 0xb1, (byte) 0xe2, (byte) 0x85, + (byte) 0xbd, (byte) 0xce, (byte) 0xba, (byte) 0x83, (byte) 0xe2, + (byte) 0x80, (byte) 0xbc }; + + static { + try { + oneOfEach = new OneOfEach(); + oneOfEach.setIm_true(true); + oneOfEach.setIm_false(false); + oneOfEach.setA_bite((byte) 0xd6); + oneOfEach.setInteger16((short) 27000); + oneOfEach.setInteger32(1 << 24); + oneOfEach.setInteger64((long) 6000 * 1000 * 1000); + oneOfEach.setDouble_precision(Math.PI); + oneOfEach.setSome_characters("JSON THIS! \"\1"); + oneOfEach.setZomg_unicode(new String(kUnicodeBytes, StandardCharsets.UTF_8)); + oneOfEach.setBase64(ByteBuffer.wrap("base64".getBytes())); + // byte, i16, and i64 lists are populated by default constructor + + Bonk bonk = new Bonk(); + bonk.setType(31337); + bonk.setMessage("I am a bonk... xor!"); + nesting = new Nesting(bonk, oneOfEach); + + holyMoley = new HolyMoley(); + List<OneOfEach> big = new ArrayList<OneOfEach>(); + big.add(new OneOfEach(oneOfEach)); + big.add(nesting.my_ooe); + holyMoley.setBig(big); + holyMoley.getBig().get(0).setA_bite((byte) 0x22); + holyMoley.getBig().get(0).setA_bite((byte) 0x23); + + holyMoley.setContain(new HashSet<List<String>>()); + ArrayList<String> stage1 = new ArrayList<String>(2); + stage1.add("and a one"); + stage1.add("and a two"); + holyMoley.getContain().add(stage1); + stage1 = new ArrayList<String>(3); + stage1.add("then a one, two"); + stage1.add("three!"); + stage1.add("FOUR!!"); + holyMoley.getContain().add(stage1); + stage1 = new ArrayList<String>(0); + holyMoley.getContain().add(stage1); + + ArrayList<Bonk> stage2 = new ArrayList<Bonk>(); + holyMoley.setBonks(new HashMap<String, List<Bonk>>()); + // one empty + holyMoley.getBonks().put("zero", stage2); + + // one with two + stage2 = new ArrayList<Bonk>(); + Bonk b = new Bonk(); + b.setType(1); + b.setMessage("Wait."); + stage2.add(b); + b = new Bonk(); + b.setType(2); + b.setMessage("What?"); + stage2.add(b); + holyMoley.getBonks().put("two", stage2); + + // one with three + stage2 = new ArrayList<Bonk>(); + b = new Bonk(); + b.setType(3); + b.setMessage("quoth"); + b = new Bonk(); + b.setType(4); + b.setMessage("the raven"); + b = new Bonk(); + b.setType(5); + b.setMessage("nevermore"); + holyMoley.getBonks().put("three", stage2); + + // superhuge compact proto test struct + compactProtoTestStruct = new CompactProtoTestStruct( + thrift.test.DebugProtoTestConstants.COMPACT_TEST); + compactProtoTestStruct.setA_binary(ByteBuffer.wrap(new byte[] { 0, 1, 2, + 3, 4, 5, 6, 7, 8 })); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java new file mode 100644 index 000000000..acafaef10 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestDeepCopy.java @@ -0,0 +1,34 @@ +package org.apache.thrift; + +import junit.framework.TestCase; +import thrift.test.DeepCopyBar; +import thrift.test.DeepCopyFoo; + +public class TestDeepCopy extends TestCase { + + public void testDeepCopy() throws Exception { + final DeepCopyFoo foo = new DeepCopyFoo(); + + foo.addToL(new DeepCopyBar()); + foo.addToS(new DeepCopyBar()); + foo.putToM("test 3", new DeepCopyBar()); + + foo.addToLi(new thrift.test.Object()); + foo.addToSi(new thrift.test.Object()); + foo.putToMi("test 3", new thrift.test.Object()); + + foo.setBar(new DeepCopyBar()); + + final DeepCopyFoo deepCopyFoo = foo.deepCopy(); + + assertNotSame(foo.getBar(), deepCopyFoo.getBar()); + + assertNotSame(foo.getL().get(0), deepCopyFoo.getL().get(0)); + assertNotSame(foo.getS().toArray(new DeepCopyBar[0])[0], deepCopyFoo.getS().toArray(new DeepCopyBar[0])[0]); + assertNotSame(foo.getM().get("test 3"), deepCopyFoo.getM().get("test 3")); + + assertNotSame(foo.getLi().get(0), deepCopyFoo.getLi().get(0)); + assertNotSame(foo.getSi().toArray(new thrift.test.Object[0])[0], deepCopyFoo.getSi().toArray(new thrift.test.Object[0])[0]); + assertNotSame(foo.getMi().get("test 3"), deepCopyFoo.getMi().get("test 3")); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java new file mode 100644 index 000000000..683246ba6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestEnumContainers.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import junit.framework.TestCase; +import thrift.test.enumcontainers.EnumContainersTestConstants; +import thrift.test.enumcontainers.GodBean; +import thrift.test.enumcontainers.GreekGodGoddess; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; + +public class TestEnumContainers extends TestCase { + + public void testEnumContainers() throws Exception { + final GodBean b1 = new GodBean(); + b1.addToGoddess(GreekGodGoddess.HERA); + b1.getGoddess().add(GreekGodGoddess.APHRODITE); + b1.putToPower(GreekGodGoddess.ZEUS, 1000); + b1.getPower().put(GreekGodGoddess.HERA, 333); + b1.putToByAlias("Mr. Z", GreekGodGoddess.ZEUS); + b1.addToImages("Baths of Aphrodite 01.jpeg"); + + final GodBean b2 = new GodBean(b1); + + final GodBean b3 = new GodBean(); + { + final TSerializer serializer = new TSerializer(); + final TDeserializer deserializer = new TDeserializer(); + + final byte[] bytes = serializer.serialize(b1); + deserializer.deserialize(b3, bytes); + } + + assertTrue(b1.getGoddess() != b2.getGoddess()); + assertTrue(b1.getPower() != b2.getPower()); + + assertTrue(b1.getGoddess() != b3.getGoddess()); + assertTrue(b1.getPower() != b3.getPower()); + + for (GodBean each : new GodBean[]{b1, b2, b3}) { + assertTrue(each.getGoddess().contains(GreekGodGoddess.HERA)); + assertFalse(each.getGoddess().contains(GreekGodGoddess.POSEIDON)); + assertTrue(each.getGoddess() instanceof EnumSet); + + assertEquals(Integer.valueOf(1000), each.getPower().get(GreekGodGoddess.ZEUS)); + assertEquals(Integer.valueOf(333), each.getPower().get(GreekGodGoddess.HERA)); + assertTrue(each.getPower() instanceof EnumMap); + + assertTrue(each.getByAlias() instanceof HashMap); + assertTrue(each.getImages() instanceof HashSet); + } + } + + public void testEnumConstants() { + assertEquals("lightning bolt", EnumContainersTestConstants.ATTRIBUTES.get(GreekGodGoddess.ZEUS)); + assertTrue(EnumContainersTestConstants.ATTRIBUTES instanceof EnumMap); + + assertTrue(EnumContainersTestConstants.BEAUTY.contains(GreekGodGoddess.APHRODITE)); + assertTrue(EnumContainersTestConstants.BEAUTY instanceof EnumSet); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java new file mode 100644 index 000000000..fc9889890 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestFullCamel.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.util.HashSet; + +import junit.framework.TestCase; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TType; + +import thrift.test.fullcamel.OneOfEachZZ; +import thrift.test.fullcamel.UnderscoreSrv; + +// Sanity check for the code generated by 'fullcamel'. +// +public class TestFullCamel extends TestCase { + + public void testCamelCaseSyntax() throws Exception { + TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); + TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); + + OneOfEachZZ obj = new OneOfEachZZ(); + obj.setABite((byte) 0xae); + obj.setImFalse(true); + byte[] serBytes = binarySerializer.serialize(obj); + binaryDeserializer.deserialize(obj, serBytes); + assertTrue( obj.getABite() == (byte) 0xae ); + assertTrue( obj.isImFalse() == true ); + } + + public void testCamelCaseRpcMethods() throws Exception { + final UnderscoreSrv.Iface srv = new UnderscoreSrv.Iface() { + @Override + public long someRpcCall(String message) { + return 1l; + } + }; + assertTrue(1l == srv.someRpcCall("test")); + } +} + diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java new file mode 100644 index 000000000..85e7966bf --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestMultiplexedProcessor.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; +import org.junit.Before; +import org.junit.Test; + +public class TestMultiplexedProcessor { + private TMultiplexedProcessor mp; + private TProtocol iprot; + private TProtocol oprot; + + @Before + public void setUp() throws Exception { + mp = new TMultiplexedProcessor(); + iprot = mock(TProtocol.class); + oprot = mock(TProtocol.class); + } + + @Test(expected = TException.class) + public void testWrongMessageType() throws TException { + when (iprot.readMessageBegin()).thenReturn(new TMessage("service:func", TMessageType.REPLY, 42)); + mp.process(iprot, oprot); + } + + @Test(expected = TException.class) + public void testNoSuchService() throws TException { + when(iprot.readMessageBegin()).thenReturn(new TMessage("service:func", TMessageType.CALL, 42)); + + mp.process(iprot, oprot); + } + + static class StubProcessor implements TProcessor { + @Override + public void process(TProtocol in, TProtocol out) throws TException { + TMessage msg = in.readMessageBegin(); + if (!"func".equals(msg.name) || msg.type!=TMessageType.CALL || msg.seqid!=42) { + throw new TException("incorrect parameters"); + } + out.writeMessageBegin(new TMessage("func", TMessageType.REPLY, 42)); + } + } + + @Test + public void testExistingService() throws TException { + when(iprot.readMessageBegin()).thenReturn(new TMessage("service:func", TMessageType.CALL, 42)); + mp.registerProcessor("service", new StubProcessor()); + mp.process(iprot, oprot); + verify(oprot).writeMessageBegin(any(TMessage.class)); + } + + @Test + public void testDefaultService() throws TException { + when(iprot.readMessageBegin()).thenReturn(new TMessage("func", TMessageType.CALL, 42)); + mp.registerDefault(new StubProcessor()); + mp.process(iprot, oprot); + verify(oprot).writeMessageBegin(any(TMessage.class)); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java new file mode 100644 index 000000000..f70285ffa --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionType.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import junit.framework.TestCase; +import org.apache.thrift.Option; + +// Tests and documents behavior for the "Option<T>" type +public class TestOptionType extends TestCase { + public void testSome() throws Exception { + String name = "Chuck Norris"; + Option<String> option = Option.fromNullable(name); + + assertTrue(option instanceof Option.Some); + assertTrue(option.isDefined()); + assertEquals("Some(Chuck Norris)", option.toString()); + assertEquals(option.or("default value"), "Chuck Norris"); + assertEquals(option.get(),"Chuck Norris"); + } + + public void testNone() throws Exception { + String name = null; + Option<String> option = Option.fromNullable(name); + + assertTrue(option instanceof Option.None); + assertFalse(option.isDefined()); + assertEquals("None", option.toString()); + assertEquals(option.or("default value"), "default value"); + // Expect exception + try { + Object value = option.get(); + fail("Expected IllegalStateException, got no exception"); + } catch (IllegalStateException ex) { + + } catch(Exception ex) { + fail("Expected IllegalStateException, got some other exception: "+ex.toString()); + } + } + + public void testMakeSome() throws Exception { + Option<String> some = Option.some("wee"); + assertTrue(some instanceof Option.Some); + } + + public void testMakeNone() throws Exception { + Option<Integer> none = Option.none(); + assertTrue(none instanceof Option.None); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java new file mode 100644 index 000000000..d1591ee2c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestOptionals.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import junit.framework.TestCase; + +import thrift.test.Opt4; +import thrift.test.Opt30; +import thrift.test.Opt64; +import thrift.test.Opt80; + +// Exercises the isSet methods using structs from from ManyOptionals.thrift +public class TestOptionals extends TestCase { + public void testEncodingUtils() throws Exception { + assertEquals((short)0x8, EncodingUtils.setBit((short)0, 3, true)); + assertEquals((short)0, EncodingUtils.setBit((short)0x8, 3, false)); + assertEquals(true, EncodingUtils.testBit((short)0x8, 3)); + assertEquals(false, EncodingUtils.testBit((short)0x8, 4)); + + assertEquals((short)Short.MIN_VALUE, EncodingUtils.setBit((short)0, 15, true)); + assertEquals((short)0, EncodingUtils.setBit((short)Short.MIN_VALUE, 15, false)); + assertEquals(true, EncodingUtils.testBit(Short.MIN_VALUE, 15)); + assertEquals(false, EncodingUtils.testBit(Short.MIN_VALUE, 14)); + } + + public void testOpt4() throws Exception { + Opt4 x = new Opt4(); + assertEquals(false, x.isSetDef1()); + x.setDef1(3); + assertEquals(true, x.isSetDef1()); + assertEquals(false, x.isSetDef2()); + + Opt4 copy = new Opt4(x); + assertEquals(true, copy.isSetDef1()); + copy.unsetDef1(); + assertEquals(false, copy.isSetDef1()); + assertEquals(true, x.isSetDef1()); + } + + public void testOpt30() throws Exception { + Opt30 x = new Opt30(); + assertEquals(false, x.isSetDef1()); + x.setDef1(3); + assertEquals(true, x.isSetDef1()); + assertEquals(false, x.isSetDef2()); + } + + public void testOpt64() throws Exception { + Opt64 x = new Opt64(); + assertEquals(false, x.isSetDef1()); + x.setDef1(3); + assertEquals(true, x.isSetDef1()); + assertEquals(false, x.isSetDef2()); + x.setDef64(22); + assertEquals(true, x.isSetDef64()); + assertEquals(false, x.isSetDef63()); + } + + public void testOpt80() throws Exception { + Opt80 x = new Opt80(); + assertEquals(false, x.isSetDef1()); + x.setDef1(3); + assertEquals(true, x.isSetDef1()); + assertEquals(false, x.isSetDef2()); + + Opt80 copy = new Opt80(x); + copy.unsetDef1(); + assertEquals(false, copy.isSetDef1()); + assertEquals(true, x.isSetDef1()); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java new file mode 100644 index 000000000..d691fe356 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestRenderedDoubleConstants.java @@ -0,0 +1,179 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.util.List; +import junit.framework.TestCase; +import static org.junit.Assert.*; +import org.junit.Test; +import thrift.test.DoubleConstantsTestConstants; + +public class TestRenderedDoubleConstants extends TestCase { + private static final double EPSILON = 0.0000001; + private static final String ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST = + "failed to verify a double constant generated by Thrift (expected = %f, got = %f)"; + private static final String ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_LIST_TEST = + "failed to verify a list item by Thrift (expected = %f, got = %f)"; + private static final String ASSERTION_MESSAGE_FOR_TYPE_CHECKS = + "the rendered variable with name %s is not of double type"; + + // to make sure lists containing doubles are generated correctly + public void testRenderedDoubleList() throws Exception { + final double[] EXPECTED_LIST = + {1d,-100d,100d,9223372036854775807d,-9223372036854775807d,3.14159265359,1000000.1,-1000000.1,1.7e+308, + -1.7e+308,9223372036854775816.43,-9223372036854775816.43}; + assertEquals(EXPECTED_LIST.length, DoubleConstantsTestConstants.DOUBLE_LIST_TEST.size()); + for (int i = 0; i < EXPECTED_LIST.length; ++i) { + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_LIST_TEST, + EXPECTED_LIST[i], + DoubleConstantsTestConstants.DOUBLE_LIST_TEST.get(i)), + EXPECTED_LIST[i], DoubleConstantsTestConstants.DOUBLE_LIST_TEST.get(i), EPSILON); + } + } + + // to make sure the variables inside Thrift files are generated correctly + public void testRenderedDoubleConstants() throws Exception { + final double EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT = 1.0; + final double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT = -100.0; + final double EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT = 9223372036854775807.0; + final double EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT = -9223372036854775807.0; + final double EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS = 3.14159265359; + final double EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE = 1000000.1; + final double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE = -1000000.1; + final double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE = 1.7e+308; + final double EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE = 9223372036854775816.43; + final double EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE = -1.7e+308; + final double EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE = -9223372036854775816.43; + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_SMALL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST, EPSILON); + assertEquals( + String.format( + ASSERTION_MESSAGE_FOR_RENDERED_DOUBLE_CONSTANTS_TEST, + EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST), + EXPECTED_DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE, + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST, EPSILON); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_INT_CONSTANT_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_INT_CONSTANT_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGEST_INT_CONSTANT_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALLEST_INT_CONSTANT_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_DOUBLE_WITH_MANY_DECIMALS_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_FRACTIONAL_DOUBLE_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_FRACTIONAL_DOUBLE_TEST)); + //assertTrue( + // String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST"), + // Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_DOUBLE_TEST)); + assertTrue( + String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST"), + Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_LARGE_FRACTIONAL_DOUBLE_TEST)); + //assertTrue( + // String.format(ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST"), + // Double.class.isInstance(DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_SMALL_DOUBLE_TEST)); + assertTrue( + String.format( + ASSERTION_MESSAGE_FOR_TYPE_CHECKS, "DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST"), + Double.class.isInstance( + DoubleConstantsTestConstants.DOUBLE_ASSIGNED_TO_NEGATIVE_BUT_LARGE_FRACTIONAL_DOUBLE_TEST)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java new file mode 100644 index 000000000..b44abd0d2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestReuse.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.util.HashSet; + +import org.apache.thrift.protocol.TBinaryProtocol; + +import thrift.test.Reuse; + +// Tests reusing objects for deserialization. +// +public class TestReuse extends TestStruct { + + public void testReuseObject() throws Exception { + TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); + TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); + + Reuse ru1 = new Reuse(); + HashSet<String> hs1 = new HashSet<String>(); + byte[] serBytes; + String st1 = new String("string1"); + String st2 = new String("string2"); + + ru1.setVal1(11); + ru1.setVal2(hs1); + ru1.addToVal2(st1); + + serBytes = binarySerializer.serialize(ru1); + + // update hash set after serialization + hs1.add(st2); + + binaryDeserializer.deserialize(ru1, serBytes); + + assertTrue( ru1.getVal2() == hs1 ); + assertTrue( hs1.size() == 2 ); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java new file mode 100644 index 000000000..3379ed120 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestStruct.java @@ -0,0 +1,396 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.thrift.meta_data.FieldMetaData; +import org.apache.thrift.meta_data.ListMetaData; +import org.apache.thrift.meta_data.MapMetaData; +import org.apache.thrift.meta_data.SetMetaData; +import org.apache.thrift.meta_data.StructMetaData; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TType; + +import thrift.test.Bonk; +import thrift.test.CrazyNesting; +import thrift.test.HolyMoley; +import thrift.test.Insanity; +import thrift.test.JavaTestHelper; +import thrift.test.Nesting; +import thrift.test.Numberz; +import thrift.test.OneOfEach; +import thrift.test.StructA; +import thrift.test.StructB; +import thrift.test.Xtruct; + +public class TestStruct extends TestCase { + public void testIdentity() throws Exception { + TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); + TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); + + OneOfEach ooe = Fixtures.oneOfEach; + + Nesting n = new Nesting(); + n.my_ooe = ooe; + n.my_ooe.integer16 = 16; + n.my_ooe.integer32 = 32; + n.my_ooe.integer64 = 64; + n.my_ooe.double_precision = (Math.sqrt(5)+1)/2; + n.my_ooe.some_characters = ":R (me going \"rrrr\")"; + n.my_ooe.zomg_unicode = "\u04c0\u216e\u039d\u0020\u041d\u03bf\u217f"+ + "\u043e\u0261\u0433\u0430\u03c1\u210e\u0020"+ + "\u0391\u0074\u0074\u03b1\u217d\u03ba\u01c3"+ + "\u203c"; + n.my_bonk = Fixtures.nesting.my_bonk; + + HolyMoley hm = Fixtures.holyMoley; + + OneOfEach ooe2 = new OneOfEach(); + binaryDeserializer.deserialize( + ooe2, + binarySerializer.serialize(ooe)); + + assertEquals(ooe, ooe2); + assertEquals(ooe.hashCode(), ooe2.hashCode()); + + + Nesting n2 = new Nesting(); + binaryDeserializer.deserialize( + n2, + binarySerializer.serialize(n)); + + assertEquals(n, n2); + assertEquals(n.hashCode(), n2.hashCode()); + + HolyMoley hm2 = new HolyMoley(); + binaryDeserializer.deserialize( + hm2, + binarySerializer.serialize(hm)); + + assertEquals(hm, hm2); + assertEquals(hm.hashCode(), hm2.hashCode()); + } + + public void testDeepCopy() throws Exception { + TSerializer binarySerializer = new TSerializer(new TBinaryProtocol.Factory()); + TDeserializer binaryDeserializer = new TDeserializer(new TBinaryProtocol.Factory()); + + HolyMoley hm = Fixtures.holyMoley; + + byte[] binaryCopy = binarySerializer.serialize(hm); + HolyMoley hmCopy = new HolyMoley(); + binaryDeserializer.deserialize(hmCopy, binaryCopy); + HolyMoley hmCopy2 = new HolyMoley(hm); + + assertEquals(hm, hmCopy); + assertEquals(hmCopy, hmCopy2); + + // change binary value in original object + hm.big.get(0).base64.array()[0]++; + // make sure the change didn't propagate to the copied object + assertFalse(hm.equals(hmCopy2)); + hm.big.get(0).base64.array()[0]--; // undo change + + hmCopy2.bonks.get("two").get(1).message = "What else?"; + + assertFalse(hm.equals(hmCopy2)); + } + + public void testCompareTo() throws Exception { + Bonk bonk1 = new Bonk(); + Bonk bonk2 = new Bonk(); + + // Compare empty thrift objects. + assertEquals(0, bonk1.compareTo(bonk2)); + + bonk1.setMessage("m"); + + // Compare one thrift object with a filled in field and another without it. + assertTrue(bonk1.compareTo(bonk2) > 0); + assertTrue(bonk2.compareTo(bonk1) < 0); + + // Compare both have filled-in fields. + bonk2.setMessage("z"); + assertTrue(bonk1.compareTo(bonk2) < 0); + assertTrue(bonk2.compareTo(bonk1) > 0); + + // Compare bonk1 has a field filled in that bonk2 doesn't. + bonk1.setType(123); + assertTrue(bonk1.compareTo(bonk2) > 0); + assertTrue(bonk2.compareTo(bonk1) < 0); + + // Compare bonk1 and bonk2 equal. + bonk2.setType(123); + bonk2.setMessage("m"); + assertEquals(0, bonk1.compareTo(bonk2)); + } + + public void testCompareToWithDataStructures() { + Insanity insanity1 = new Insanity(); + Insanity insanity2 = new Insanity(); + + // Both empty. + expectEquals(insanity1, insanity2); + + insanity1.setUserMap(new HashMap<Numberz, Long>()); + // insanity1.map = {}, insanity2.map = null + expectGreaterThan(insanity1, insanity2); + + // insanity1.map = {2:1}, insanity2.map = null + insanity1.getUserMap().put(Numberz.TWO, 1l); + expectGreaterThan(insanity1, insanity2); + + // insanity1.map = {2:1}, insanity2.map = {} + insanity2.setUserMap(new HashMap<Numberz, Long>()); + expectGreaterThan(insanity1, insanity2); + + // insanity1.map = {2:1}, insanity2.map = {2:2} + insanity2.getUserMap().put(Numberz.TWO, 2l); + expectLessThan(insanity1, insanity2); + + // insanity1.map = {2:1, 3:5}, insanity2.map = {2:2} + insanity1.getUserMap().put(Numberz.THREE, 5l); + expectGreaterThan(insanity1, insanity2); + + // insanity1.map = {2:1, 3:5}, insanity2.map = {2:1, 4:5} + insanity2.getUserMap().put(Numberz.TWO, 1l); + insanity2.getUserMap().put(Numberz.FIVE, 5l); + expectLessThan(insanity1, insanity2); + } + + private void expectLessThan(Insanity insanity1, Insanity insanity2) { + int compareTo = insanity1.compareTo(insanity2); + assertTrue(insanity1 + " should be less than " + insanity2 + ", but is: " + compareTo, compareTo < 0); + } + + private void expectGreaterThan(Insanity insanity1, Insanity insanity2) { + int compareTo = insanity1.compareTo(insanity2); + assertTrue(insanity1 + " should be greater than " + insanity2 + ", but is: " + compareTo, compareTo > 0); + } + + private void expectEquals(Insanity insanity1, Insanity insanity2) { + int compareTo = insanity1.compareTo(insanity2); + assertEquals(insanity1 + " should be equal to " + insanity2 + ", but is: " + compareTo, 0, compareTo); + } + + public void testMetaData() throws Exception { + Map<CrazyNesting._Fields, FieldMetaData> mdMap = CrazyNesting.metaDataMap; + + // Check for struct fields existence + assertEquals(4, mdMap.size()); + assertTrue(mdMap.containsKey(CrazyNesting._Fields.SET_FIELD)); + assertTrue(mdMap.containsKey(CrazyNesting._Fields.LIST_FIELD)); + assertTrue(mdMap.containsKey(CrazyNesting._Fields.STRING_FIELD)); + assertTrue(mdMap.containsKey(CrazyNesting._Fields.BINARY_FIELD)); + + // Check for struct fields contents + assertEquals("string_field", mdMap.get(CrazyNesting._Fields.STRING_FIELD).fieldName); + assertEquals("list_field", mdMap.get(CrazyNesting._Fields.LIST_FIELD).fieldName); + assertEquals("set_field", mdMap.get(CrazyNesting._Fields.SET_FIELD).fieldName); + assertEquals("binary_field", mdMap.get(CrazyNesting._Fields.BINARY_FIELD).fieldName); + + assertEquals(TFieldRequirementType.DEFAULT, mdMap.get(CrazyNesting._Fields.STRING_FIELD).requirementType); + assertEquals(TFieldRequirementType.REQUIRED, mdMap.get(CrazyNesting._Fields.LIST_FIELD).requirementType); + assertEquals(TFieldRequirementType.OPTIONAL, mdMap.get(CrazyNesting._Fields.SET_FIELD).requirementType); + + assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.STRING_FIELD).valueMetaData.type); + assertFalse(mdMap.get(CrazyNesting._Fields.STRING_FIELD).valueMetaData.isBinary()); + assertEquals(TType.LIST, mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.type); + assertEquals(TType.SET, mdMap.get(CrazyNesting._Fields.SET_FIELD).valueMetaData.type); + assertEquals(TType.STRING, mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.type); + assertTrue(mdMap.get(CrazyNesting._Fields.BINARY_FIELD).valueMetaData.isBinary()); + + // Check nested structures + assertTrue(mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.isContainer()); + + assertFalse(mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData.isStruct()); + + assertEquals(TType.STRUCT, ((MapMetaData)((ListMetaData)((SetMetaData)((MapMetaData)((MapMetaData)((ListMetaData)mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData).elemMetaData).valueMetaData).valueMetaData).elemMetaData).elemMetaData).keyMetaData.type); + + assertEquals(Insanity.class, ((StructMetaData)((MapMetaData)((ListMetaData)((SetMetaData)((MapMetaData)((MapMetaData)((ListMetaData)mdMap.get(CrazyNesting._Fields.LIST_FIELD).valueMetaData).elemMetaData).valueMetaData).valueMetaData).elemMetaData).elemMetaData).keyMetaData).structClass); + + // Check that FieldMetaData contains a map with metadata for all generated struct classes + assertNotNull(FieldMetaData.getStructMetaDataMap(CrazyNesting.class)); + assertNotNull(FieldMetaData.getStructMetaDataMap(Insanity.class)); + assertNotNull(FieldMetaData.getStructMetaDataMap(Xtruct.class)); + + assertEquals(CrazyNesting.metaDataMap, FieldMetaData.getStructMetaDataMap(CrazyNesting.class)); + assertEquals(Insanity.metaDataMap, FieldMetaData.getStructMetaDataMap(Insanity.class)); + + for (Map.Entry<? extends TFieldIdEnum, FieldMetaData> mdEntry : mdMap.entrySet()) { + assertEquals(mdEntry.getKey(), CrazyNesting._Fields.findByName(mdEntry.getValue().fieldName)); + } + + MapMetaData vmd = (MapMetaData)Insanity.metaDataMap.get(Insanity._Fields.USER_MAP).valueMetaData; + assertTrue(vmd.valueMetaData.isTypedef()); + assertFalse(vmd.keyMetaData.isTypedef()); + } + + public void testToString() throws Exception { + JavaTestHelper object = new JavaTestHelper(); + object.req_int = 0; + object.req_obj = ""; + + object.req_bin = ByteBuffer.wrap(new byte[] { + 0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, + 16, -17, 18, -19, 20, -21, 22, -23, 24, -25, 26, -27, 28, -29, + 30, -31, 32, -33, 34, -35, 36, -37, 38, -39, 40, -41, 42, -43, 44, + -45, 46, -47, 48, -49, 50, -51, 52, -53, 54, -55, 56, -57, 58, -59, + 60, -61, 62, -63, 64, -65, 66, -67, 68, -69, 70, -71, 72, -73, 74, + -75, 76, -77, 78, -79, 80, -81, 82, -83, 84, -85, 86, -87, 88, -89, + 90, -91, 92, -93, 94, -95, 96, -97, 98, -99, 100, -101, 102, -103, + 104, -105, 106, -107, 108, -109, 110, -111, 112, -113, 114, -115, + 116, -117, 118, -119, 120, -121, 122, -123, 124, -125, 126, -127, + }); + + assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:"+ + "00 FF 02 FD 04 FB 06 F9 08 F7 0A F5 0C F3 0E F1 10 EF 12 ED 14 "+ + "EB 16 E9 18 E7 1A E5 1C E3 1E E1 20 DF 22 DD 24 DB 26 D9 28 D7 "+ + "2A D5 2C D3 2E D1 30 CF 32 CD 34 CB 36 C9 38 C7 3A C5 3C C3 3E "+ + "C1 40 BF 42 BD 44 BB 46 B9 48 B7 4A B5 4C B3 4E B1 50 AF 52 AD "+ + "54 AB 56 A9 58 A7 5A A5 5C A3 5E A1 60 9F 62 9D 64 9B 66 99 68 "+ + "97 6A 95 6C 93 6E 91 70 8F 72 8D 74 8B 76 89 78 87 7A 85 7C 83 "+ + "7E 81)", + object.toString()); + + object.req_bin = ByteBuffer.wrap(new byte[] { + 0, -1, 2, -3, 4, -5, 6, -7, 8, -9, 10, -11, 12, -13, 14, -15, + 16, -17, 18, -19, 20, -21, 22, -23, 24, -25, 26, -27, 28, -29, + 30, -31, 32, -33, 34, -35, 36, -37, 38, -39, 40, -41, 42, -43, 44, + -45, 46, -47, 48, -49, 50, -51, 52, -53, 54, -55, 56, -57, 58, -59, + 60, -61, 62, -63, 64, -65, 66, -67, 68, -69, 70, -71, 72, -73, 74, + -75, 76, -77, 78, -79, 80, -81, 82, -83, 84, -85, 86, -87, 88, -89, + 90, -91, 92, -93, 94, -95, 96, -97, 98, -99, 100, -101, 102, -103, + 104, -105, 106, -107, 108, -109, 110, -111, 112, -113, 114, -115, + 116, -117, 118, -119, 120, -121, 122, -123, 124, -125, 126, -127, + 0, + }); + + assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:"+ + "00 FF 02 FD 04 FB 06 F9 08 F7 0A F5 0C F3 0E F1 10 EF 12 ED 14 "+ + "EB 16 E9 18 E7 1A E5 1C E3 1E E1 20 DF 22 DD 24 DB 26 D9 28 D7 "+ + "2A D5 2C D3 2E D1 30 CF 32 CD 34 CB 36 C9 38 C7 3A C5 3C C3 3E "+ + "C1 40 BF 42 BD 44 BB 46 B9 48 B7 4A B5 4C B3 4E B1 50 AF 52 AD "+ + "54 AB 56 A9 58 A7 5A A5 5C A3 5E A1 60 9F 62 9D 64 9B 66 99 68 "+ + "97 6A 95 6C 93 6E 91 70 8F 72 8D 74 8B 76 89 78 87 7A 85 7C 83 "+ + "7E 81...)", + object.toString()); + + object.req_bin = ByteBuffer.wrap(new byte[] {}); + object.setOpt_binIsSet(true); + + assertEquals("JavaTestHelper(req_int:0, req_obj:, req_bin:)", + object.toString()); + } + + private static void assertArrayEquals(byte[] expected, byte[] actual) { + if (!java.util.Arrays.equals(expected, actual)) { + fail("Expected byte array did not match actual."); + } + } + + public void testBytesBufferFeatures() throws Exception { + + final String testString = "testBytesBufferFeatures"; + final JavaTestHelper o = new JavaTestHelper(); + + o.setReq_bin((ByteBuffer)null); + assertNull(o.getReq_bin()); + + o.setReq_bin(ByteBuffer.wrap(testString.getBytes())); + assertArrayEquals(testString.getBytes(), o.getReq_bin()); + + o.setReq_bin((byte[])null); + assertNull(o.getReq_bin()); + + o.setReq_bin(testString.getBytes()); + assertArrayEquals(testString.getBytes(), o.getReq_bin()); + + o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, null); + assertNull(o.getReq_bin()); + + o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, testString.getBytes()); + assertArrayEquals(testString.getBytes(), o.getReq_bin()); + + o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, null); + assertNull(o.getReq_bin()); + + o.setFieldValue(JavaTestHelper._Fields.REQ_BIN, ByteBuffer.wrap(testString.getBytes())); + assertArrayEquals(testString.getBytes(), o.getReq_bin()); + } + + public void testJavaSerializable() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + + OneOfEach ooe = Fixtures.oneOfEach; + + // Serialize ooe the Java way... + oos.writeObject(ooe); + byte[] serialized = baos.toByteArray(); + + // Attempt to deserialize it + ByteArrayInputStream bais = new ByteArrayInputStream(serialized); + ObjectInputStream ois = new ObjectInputStream(bais); + OneOfEach ooe2 = (OneOfEach) ois.readObject(); + + assertEquals(ooe, ooe2); + } + + public void testSubStructValidation() throws Exception { + StructA valid = new StructA("valid"); + StructA invalid = new StructA(); + + StructB b = new StructB(); + try { + b.validate(); + fail(); + } catch (TException e) { + // expected + } + + b = new StructB().setAb(valid); + b.validate(); + + b = new StructB().setAb(invalid); + try { + b.validate(); + fail(); + } catch (TException e) { + // expected + } + + b = new StructB().setAb(valid).setAa(invalid); + try { + b.validate(); + fail(); + } catch (TException e) { + // expected + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java new file mode 100644 index 000000000..8b0855525 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTBaseHelper.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +public class TestTBaseHelper extends TestCase { + public void testByteArrayComparison() { + assertTrue(TBaseHelper.compareTo(new byte[]{'a','b'}, new byte[]{'a','c'}) < 0); + } + + public void testSets() { + Set<String> a = new HashSet<String>(); + Set<String> b = new HashSet<String>(); + + assertTrue(TBaseHelper.compareTo(a, b) == 0); + + a.add("test"); + + assertTrue(TBaseHelper.compareTo(a, b) > 0); + + b.add("test"); + + assertTrue(TBaseHelper.compareTo(a, b) == 0); + + b.add("aardvark"); + + assertTrue(TBaseHelper.compareTo(a, b) < 0); + + a.add("test2"); + + assertTrue(TBaseHelper.compareTo(a, b) > 0); + } + + public void testNestedStructures() { + Set<List<String>> a = new HashSet<List<String>>(); + Set<List<String>> b = new HashSet<List<String>>(); + + a.add(Arrays.asList(new String[] {"a","b"})); + b.add(Arrays.asList(new String[] {"a","b", "c"})); + a.add(Arrays.asList(new String[] {"a","b"})); + b.add(Arrays.asList(new String[] {"a","b", "c"})); + + assertTrue(TBaseHelper.compareTo(a, b) < 0); + } + + public void testMapsInSets() { + Set<Map<String, Long>> a = new HashSet<Map<String, Long>>(); + Set<Map<String, Long>> b = new HashSet<Map<String, Long>>(); + + assertTrue(TBaseHelper.compareTo(a, b) == 0); + + Map<String, Long> innerA = new HashMap<String, Long>(); + Map<String, Long> innerB = new HashMap<String, Long>(); + a.add(innerA); + b.add(innerB); + + innerA.put("a", 1l); + innerB.put("a", 2l); + + assertTrue(TBaseHelper.compareTo(a, b) < 0); + } + + public void testByteArraysInMaps() { + Map<byte[], Long> a = new HashMap<byte[], Long>(); + Map<byte[], Long> b = new HashMap<byte[], Long>(); + + assertTrue(TBaseHelper.compareTo(a, b) == 0); + + a.put(new byte[]{'a','b'}, 1000L); + b.put(new byte[]{'a','b'}, 1000L); + a.put(new byte[]{'a','b', 'd'}, 1000L); + b.put(new byte[]{'a','b', 'a'}, 1000L); + assertTrue(TBaseHelper.compareTo(a, b) > 0); + } + + public void testMapsWithNulls() { + Map<String, String> a = new HashMap<String, String>(); + Map<String, String> b = new HashMap<String, String>(); + a.put("a", null); + a.put("b", null); + b.put("a", null); + b.put("b", null); + + assertTrue(TBaseHelper.compareTo(a, b) == 0); + } + + public void testMapKeyComparison() { + Map<String, String> a = new HashMap<String, String>(); + Map<String, String> b = new HashMap<String, String>(); + a.put("a", "a"); + b.put("b", "a"); + + assertTrue(TBaseHelper.compareTo(a, b) < 0); + } + + public void testMapValueComparison() { + Map<String, String> a = new HashMap<String, String>(); + Map<String, String> b = new HashMap<String, String>(); + a.put("a", "b"); + b.put("a", "a"); + + assertTrue(TBaseHelper.compareTo(a, b) > 0); + } + + public void testByteArraysInSets() { + Set<byte[]> a = new HashSet<byte[]>(); + Set<byte[]> b = new HashSet<byte[]>(); + + if (TBaseHelper.compareTo(a, b) != 0) + throw new RuntimeException("Set compare failed:" + a + " vs. " + b); + + a.add(new byte[]{'a','b'}); + b.add(new byte[]{'a','b'}); + a.add(new byte[]{'a','b', 'd'}); + b.add(new byte[]{'a','b', 'a'}); + assertTrue(TBaseHelper.compareTo(a, b) > 0); + } + + public void testByteBufferToByteArray() throws Exception { + byte[] b1 = {10,9,8,7,6,5,4,3,2,1,0}; + byte[] b2 = TBaseHelper.byteBufferToByteArray(ByteBuffer.wrap(b1)); + assertEquals("b1 and b2 should be the exact same array (identity) due to fast path", b1, b2); + + byte[] b3 = TBaseHelper.byteBufferToByteArray(ByteBuffer.wrap(b1, 1, 3)); + assertEquals(3, b3.length); + assertEquals(ByteBuffer.wrap(b1, 1, 3), ByteBuffer.wrap(b3)); + } + + public void testRightSize() throws Exception { + assertNull(TBaseHelper.rightSize(null)); + } + + public void testByteBufferToString() { + byte[] array = new byte[]{1, 2, 3}; + ByteBuffer bb = ByteBuffer.wrap(array, 1, 2); + StringBuilder sb = new StringBuilder(); + TBaseHelper.toString(bb, sb); + assertEquals("02 03", sb.toString()); + bb = ByteBuffer.wrap(array, 0, array.length); + bb.position(1); + bb = bb.slice(); + assertEquals(1, bb.arrayOffset()); + assertEquals(0, bb.position()); + assertEquals(2, bb.limit()); + sb = new StringBuilder(); + TBaseHelper.toString(bb, sb); + assertEquals("02 03", sb.toString()); + } + + public void testCopyBinaryWithByteBuffer() throws Exception { + byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5}; + ByteBuffer b = ByteBuffer.wrap(bytes); + ByteBuffer bCopy = TBaseHelper.copyBinary(b); + assertEquals(b, bCopy); + assertEquals(0, b.position()); + + b = ByteBuffer.allocateDirect(6); + b.put(bytes); + b.position(0); + bCopy = TBaseHelper.copyBinary(b); + assertEquals(6, b.remaining()); + assertEquals(0, b.position()); + assertEquals(b, bCopy); + + b.mark(); + b.get(); + bCopy = TBaseHelper.copyBinary(b); + assertEquals(ByteBuffer.wrap(bytes, 1, 5), bCopy); + assertEquals(1, b.position()); + b.reset(); + assertEquals(0, b.position()); + + assertNull(TBaseHelper.copyBinary((ByteBuffer)null)); + } + + public void testCopyBinaryWithByteArray() throws Exception { + byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5}; + byte[] copy = TBaseHelper.copyBinary(bytes); + assertEquals(ByteBuffer.wrap(bytes), ByteBuffer.wrap(copy)); + assertNotSame(bytes, copy); + + assertNull(TBaseHelper.copyBinary((byte[])null)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java new file mode 100644 index 000000000..a4a353d6c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTDeserializer.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.nio.ByteBuffer; + +import junit.framework.TestCase; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.protocol.TProtocolFactory; + +import thrift.test.Backwards; +import thrift.test.OneOfEach; +import thrift.test.PrimitiveThenStruct; +import thrift.test.StructWithAUnion; +import thrift.test.TestUnion; + +public class TestTDeserializer extends TestCase { + + private static final TProtocolFactory[] PROTOCOLS = new TProtocolFactory[] { + new TBinaryProtocol.Factory(), + new TCompactProtocol.Factory(), + new TJSONProtocol.Factory() + }; + + public void testPartialDeserialize() throws Exception { + //Root:StructWithAUnion + // 1:Union + // 1.3:OneOfEach + OneOfEach level3OneOfEach = Fixtures.oneOfEach; + TestUnion level2TestUnion = new TestUnion(TestUnion._Fields.STRUCT_FIELD, level3OneOfEach); + StructWithAUnion level1SWU = new StructWithAUnion(level2TestUnion); + + Backwards bw = new Backwards(2, 1); + PrimitiveThenStruct pts = new PrimitiveThenStruct(12345, 67890, bw); + + for (TProtocolFactory factory : PROTOCOLS) { + + //Level 2 test + testPartialDeserialize(factory, level1SWU, new TestUnion(), level2TestUnion, StructWithAUnion._Fields.TEST_UNION); + + //Level 3 on 3rd field test + testPartialDeserialize(factory, level1SWU, new OneOfEach(), level3OneOfEach, StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD); + + //Test early termination when traversed path Field.id exceeds the one being searched for + testPartialDeserialize(factory, level1SWU, new OneOfEach(), new OneOfEach(), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.I32_FIELD); + + //Test that readStructBegin isn't called on primitive + testPartialDeserialize(factory, pts, new Backwards(), bw, PrimitiveThenStruct._Fields.BW); + + //Test primitive types + TDeserializer deserializer = new TDeserializer(factory); + + Boolean expectedBool = level3OneOfEach.isIm_true(); + Boolean resultBool = deserializer.partialDeserializeBool(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.IM_TRUE); + assertEquals(expectedBool, resultBool); + + Byte expectedByte = level3OneOfEach.getA_bite(); + Byte resultByte = deserializer.partialDeserializeByte(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.A_BITE); + assertEquals(expectedByte, resultByte); + + Double expectedDouble = level3OneOfEach.getDouble_precision(); + Double resultDouble = deserializer.partialDeserializeDouble(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.DOUBLE_PRECISION); + assertEquals(expectedDouble, resultDouble); + + Short expectedI16 = level3OneOfEach.getInteger16(); + Short resultI16 = deserializer.partialDeserializeI16(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.INTEGER16); + assertEquals(expectedI16, resultI16); + + Integer expectedI32 = level3OneOfEach.getInteger32(); + Integer resultI32 = deserializer.partialDeserializeI32(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.INTEGER32); + assertEquals(expectedI32, resultI32); + + Long expectedI64 = level3OneOfEach.getInteger64(); + Long resultI64= deserializer.partialDeserializeI64(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.INTEGER64); + assertEquals(expectedI64, resultI64); + + String expectedString = level3OneOfEach.getSome_characters(); + String resultString = deserializer.partialDeserializeString(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.SOME_CHARACTERS); + assertEquals(expectedString, resultString); + + byte[] expectedBinary = level3OneOfEach.getBase64(); + ByteBuffer resultBinary = deserializer.partialDeserializeByteArray(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION, TestUnion._Fields.STRUCT_FIELD, OneOfEach._Fields.BASE64); + assertEquals(expectedBinary.length, resultBinary.limit() - resultBinary.position() - resultBinary.arrayOffset()); + assertEquals(ByteBuffer.wrap(expectedBinary), resultBinary); + + // Test field id in Union + short id = deserializer.partialDeserializeSetFieldIdInUnion(serialize(level1SWU, factory), StructWithAUnion._Fields.TEST_UNION); + assertEquals(level2TestUnion.getSetField().getThriftFieldId(), id); + } + } + + public static void testPartialDeserialize(TProtocolFactory protocolFactory, TBase input, TBase output, TBase expected, TFieldIdEnum fieldIdPathFirst, TFieldIdEnum ... fieldIdPathRest) throws TException { + byte[] record = serialize(input, protocolFactory); + TDeserializer deserializer = new TDeserializer(protocolFactory); + for (int i = 0; i < 2; i++) { + TBase outputCopy = output.deepCopy(); + deserializer.partialDeserialize(outputCopy, record, fieldIdPathFirst, fieldIdPathRest); + assertEquals("on attempt " + i + ", with " + protocolFactory.toString() + + ", expected " + expected + " but got " + outputCopy, + expected, outputCopy); + } + } + + private static byte[] serialize(TBase input, TProtocolFactory protocolFactory) throws TException{ + return new TSerializer(protocolFactory).serialize(input); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java new file mode 100644 index 000000000..e2cca4039 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTEnumHelper.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import thrift.test.Numberz; + +import junit.framework.TestCase; + +public class TestTEnumHelper extends TestCase { + + public void testGetByValue_ValidValues() { + for (Numberz n: Numberz.values()) { + int value = n.getValue(); + assertEquals(n, TEnumHelper.getByValue(Numberz.class, value)); + } + } + + public void testGetByValue_InvalidValue() { + assertEquals(null, TEnumHelper.getByValue(Numberz.class, 0)); + } + + public void testGetByValue_InvalidClass() { + assertEquals(null, TEnumHelper.getByValue(TEnum.class, 0)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java new file mode 100644 index 000000000..f1e6f0e1f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestTUnion.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TTupleProtocol; +import org.apache.thrift.transport.TMemoryBuffer; + +import thrift.test.ComparableUnion; +import thrift.test.Empty; +import thrift.test.RandomStuff; +import thrift.test.SomeEnum; +import thrift.test.StructWithAUnion; +import thrift.test.TestUnion; +import thrift.test.TestUnionMinusStringField; + +public class TestTUnion extends TestCase { + + public void testBasic() throws Exception { + TestUnion union = new TestUnion(); + + assertFalse(union.isSet()); + assertFalse(union.isSetI32_field()); + assertNull(union.getFieldValue()); + + union = new TestUnion(TestUnion._Fields.I32_FIELD, 25); + + assertEquals(Integer.valueOf(25), union.getFieldValue()); + + assertEquals(Integer.valueOf(25), union.getFieldValue(TestUnion._Fields.I32_FIELD)); + + assertTrue(union.isSetI32_field()); + + try { + union.getFieldValue(TestUnion._Fields.STRING_FIELD); + fail("should have thrown an exception"); + } catch (IllegalArgumentException e) { + // cool! + } + + union = new TestUnion(); + + // should not throw an exception here + union.hashCode(); + + union.setI32_field(1); + assertEquals(1, union.getI32_field()); + union.hashCode(); + + assertFalse(union.isSetString_field()); + + try { + union.getString_field(); + fail("should have thrown an exception"); + } catch (Exception e) { + // sweet + } + + union = TestUnion.i32_field(1); + + assertFalse(union.equals((TestUnion)null)); + + union = TestUnion.enum_field(SomeEnum.ONE); + union.hashCode(); + + union = new TestUnion(); + // should not throw an exception + union.toString(); + } + + public void testCompareTo() throws Exception { + ComparableUnion cu = ComparableUnion.string_field("a"); + ComparableUnion cu2 = ComparableUnion.string_field("b"); + + assertTrue(cu.compareTo(cu) == 0); + assertTrue(cu2.compareTo(cu2) == 0); + + assertTrue(cu.compareTo(cu2) < 0); + assertTrue(cu2.compareTo(cu) > 0); + + cu2 = ComparableUnion.binary_field(ByteBuffer.wrap(new byte[]{2})); + + assertTrue(cu.compareTo(cu2) < 0); + assertTrue(cu2.compareTo(cu) > 0); + + cu = ComparableUnion.binary_field(ByteBuffer.wrap(new byte[]{1})); + + assertTrue(cu.compareTo(cu2) < 0); + assertTrue(cu2.compareTo(cu) > 0); + + TestUnion union1 = new TestUnion(TestUnion._Fields.STRUCT_LIST, new ArrayList<RandomStuff>()); + TestUnion union2 = new TestUnion(TestUnion._Fields.STRUCT_LIST, new ArrayList<RandomStuff>()); + assertTrue(union1.compareTo(union2) == 0); + + TestUnion union3 = new TestUnion(TestUnion._Fields.I32_SET, new HashSet<Integer>()); + Set<Integer> i32_set = new HashSet<Integer>(); + i32_set.add(1); + TestUnion union4 = new TestUnion(TestUnion._Fields.I32_SET, i32_set); + assertTrue(union3.compareTo(union4) < 0); + + Map<Integer, Integer> i32_map = new HashMap<Integer, Integer>(); + i32_map.put(1,1); + TestUnion union5 = new TestUnion(TestUnion._Fields.I32_MAP, i32_map); + TestUnion union6 = new TestUnion(TestUnion._Fields.I32_MAP, new HashMap<Integer, Integer>()); + assertTrue(union5.compareTo(union6) > 0); + } + + public void testEquality() throws Exception { + TestUnion union = new TestUnion(TestUnion._Fields.I32_FIELD, 25); + + TestUnion otherUnion = new TestUnion(TestUnion._Fields.STRING_FIELD, "blah!!!"); + + assertFalse(union.equals(otherUnion)); + + otherUnion = new TestUnion(TestUnion._Fields.I32_FIELD, 400); + + assertFalse(union.equals(otherUnion)); + + otherUnion = new TestUnion(TestUnion._Fields.OTHER_I32_FIELD, 25); + + assertFalse(union.equals(otherUnion)); + } + + public void testSerialization() throws Exception { + TestUnion union = new TestUnion(TestUnion._Fields.I32_FIELD, 25); + union.setI32_set(Collections.singleton(42)); + + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = new TBinaryProtocol(buf); + + union.write(proto); + + TestUnion u2 = new TestUnion(); + + u2.read(proto); + + assertEquals(u2, union); + + StructWithAUnion swau = new StructWithAUnion(u2); + + buf = new TMemoryBuffer(0); + proto = new TBinaryProtocol(buf); + + swau.write(proto); + + StructWithAUnion swau2 = new StructWithAUnion(); + assertFalse(swau2.equals(swau)); + swau2.read(proto); + assertEquals(swau2, swau); + + // this should NOT throw an exception. + buf = new TMemoryBuffer(0); + proto = new TBinaryProtocol(buf); + + swau.write(proto); + new Empty().read(proto); + } + + public void testTupleProtocolSerialization () throws Exception { + TestUnion union = new TestUnion(TestUnion._Fields.I32_FIELD, 25); + union.setI32_set(Collections.singleton(42)); + + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = new TTupleProtocol(buf); + + union.write(proto); + + TestUnion u2 = new TestUnion(); + + u2.read(proto); + + assertEquals(u2, union); + + StructWithAUnion swau = new StructWithAUnion(u2); + + buf = new TMemoryBuffer(0); + proto = new TBinaryProtocol(buf); + + swau.write(proto); + + StructWithAUnion swau2 = new StructWithAUnion(); + assertFalse(swau2.equals(swau)); + swau2.read(proto); + assertEquals(swau2, swau); + + // this should NOT throw an exception. + buf = new TMemoryBuffer(0); + proto = new TTupleProtocol(buf); + + swau.write(proto); + new Empty().read(proto); + } + + public void testSkip() throws Exception { + TestUnion tu = TestUnion.string_field("string"); + byte[] tuSerialized = new TSerializer().serialize(tu); + TestUnionMinusStringField tums = new TestUnionMinusStringField(); + new TDeserializer().deserialize(tums, tuSerialized); + assertNull(tums.getSetField()); + assertNull(tums.getFieldValue()); + } + + public void testDeepCopy() throws Exception { + byte[] bytes = {1, 2, 3}; + ByteBuffer value = ByteBuffer.wrap(bytes); + ComparableUnion cu = ComparableUnion.binary_field(value); + ComparableUnion copy = cu.deepCopy(); + assertEquals(cu, copy); + assertNotSame(cu.bufferForBinary_field().array(), copy.bufferForBinary_field().array()); + } + + public void testToString() throws Exception { + byte[] bytes = {1, 2, 3}; + ByteBuffer value = ByteBuffer.wrap(bytes); + ComparableUnion cu = ComparableUnion.binary_field(value); + String expectedString = "<ComparableUnion binary_field:01 02 03>"; + assertEquals(expectedString, cu.toString()); + } + + public void testJavaSerializable() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + + TestUnion tu = TestUnion.string_field("string"); + + // Serialize tu the Java way... + oos.writeObject(tu); + byte[] serialized = baos.toByteArray(); + + // Attempt to deserialize it + ByteArrayInputStream bais = new ByteArrayInputStream(serialized); + ObjectInputStream ois = new ObjectInputStream(bais); + TestUnion tu2 = (TestUnion) ois.readObject(); + + assertEquals(tu, tu2); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java new file mode 100644 index 000000000..d1fc21368 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/TestUnsafeBinaries.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import thrift.test.SafeBytes; +import thrift.test.UnsafeBytes; + +// test generating types with un-copied byte[]/ByteBuffer input/output +// +public class TestUnsafeBinaries extends TestStruct { + + private static byte[] input() { + return new byte[]{1, 1}; + } + + // + // verify that the unsafe_binaries option modifies behavior + // + + // constructor doesn't copy + public void testUnsafeConstructor() throws Exception { + + byte[] input = input(); + UnsafeBytes struct = new UnsafeBytes(ByteBuffer.wrap(input)); + + input[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{2, 1}, + struct.getBytes()) + ); + + } + + // getter doesn't copy + // note: this behavior is the same with/without the flag, but if this default ever changes, the current behavior + // should be retained when using this flag + public void testUnsafeGetter(){ + UnsafeBytes struct = new UnsafeBytes(ByteBuffer.wrap(input())); + + byte[] val = struct.getBytes(); + val[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{2, 1}, + struct.getBytes()) + ); + + } + + // setter doesn't copy + public void testUnsafeSetter(){ + UnsafeBytes struct = new UnsafeBytes(); + + byte[] val = input(); + struct.setBytes(val); + + val[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{2, 1}, + struct.getBytes()) + ); + + } + + // buffer doens't copy + public void testUnsafeBufferFor(){ + UnsafeBytes struct = new UnsafeBytes(ByteBuffer.wrap(input())); + + ByteBuffer val = struct.bufferForBytes(); + val.array()[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{2, 1}, + struct.getBytes()) + ); + + } + + // + // verify that the default generator does not change behavior + // + + public void testSafeConstructor() { + + byte[] input = input(); + SafeBytes struct = new SafeBytes(ByteBuffer.wrap(input)); + + input[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{1, 1}, + struct.getBytes()) + ); + + } + + public void testSafeSetter() { + + byte[] input = input(); + SafeBytes struct = new SafeBytes(ByteBuffer.wrap(input)); + + input[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{1, 1}, + struct.getBytes()) + ); + + } + + public void testSafeBufferFor(){ + SafeBytes struct = new SafeBytes(ByteBuffer.wrap(input())); + + ByteBuffer val = struct.bufferForBytes(); + val.array()[0] = 2; + + assertTrue(Arrays.equals( + new byte[]{1, 1}, + struct.getBytes()) + ); + + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java new file mode 100644 index 000000000..392ca22d4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClient.java @@ -0,0 +1,28 @@ +package org.apache.thrift.async; + +import junit.framework.TestCase; + +import org.apache.thrift.TException; + +import thrift.test.Srv; +import thrift.test.Srv.AsyncClient; + +public class TestTAsyncClient extends TestCase { + public void testRaisesExceptionWhenUsedConcurrently() throws Exception { + TAsyncClientManager mockClientManager = new TAsyncClientManager() { + @Override + public void call(TAsyncMethodCall method) throws TException { + // do nothing + } + }; + + Srv.AsyncClient c = new AsyncClient(null, mockClientManager, null); + c.Janky(0, null); + try { + c.checkReady(); + fail("should have hit an exception"); + } catch (Exception e) { + // awesome + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java new file mode 100644 index 000000000..c483cf24e --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/async/TestTAsyncClientManager.java @@ -0,0 +1,378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.async; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.TestCase; + +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.server.ServerTestBase; +import org.apache.thrift.server.THsHaServer; +import org.apache.thrift.server.THsHaServer.Args; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.transport.TNonblockingSocket; + +import thrift.test.CompactProtoTestStruct; +import thrift.test.ExceptionWithAMap; +import thrift.test.Srv; +import thrift.test.Srv.Iface; + +public class TestTAsyncClientManager extends TestCase { + + private THsHaServer server_; + private Thread serverThread_; + private TAsyncClientManager clientManager_; + + public void setUp() throws Exception { + server_ = new THsHaServer(new Args(new TNonblockingServerSocket( + new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(ServerTestBase.PORT))). + processor(new Srv.Processor(new SrvHandler()))); + serverThread_ = new Thread(new Runnable() { + public void run() { + server_.serve(); + } + }); + serverThread_.start(); + clientManager_ = new TAsyncClientManager(); + Thread.sleep(500); + } + + public void tearDown() throws Exception { + server_.stop(); + clientManager_.stop(); + serverThread_.join(); + } + + public void testBasicCall() throws Exception { + Srv.AsyncClient client = getClient(); + basicCall(client); + } + + public void testBasicCallWithTimeout() throws Exception { + Srv.AsyncClient client = getClient(); + client.setTimeout(5000); + basicCall(client); + } + + private static abstract class ErrorCallTest<C extends TAsyncClient, R> { + final void runTest() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicReference<Exception> error = new AtomicReference<Exception>(); + C client = executeErroringCall(new AsyncMethodCallback<R>() { + @Override + public void onComplete(R response) { + latch.countDown(); + } + + @Override + public void onError(Exception exception) { + error.set(exception); + latch.countDown(); + } + }); + latch.await(2, TimeUnit.SECONDS); + assertTrue(client.hasError()); + Exception exception = error.get(); + assertNotNull(exception); + assertSame(exception, client.getError()); + validateError(client, exception); + } + + /** + * Executes a call that is expected to raise an exception. + * + * @param callback The testing callback that should be installed. + * @return The client the call was made against. + * @throws Exception if there was a problem setting up the client or making the call. + */ + abstract C executeErroringCall(AsyncMethodCallback<R> callback) throws Exception; + + /** + * Further validates the properties of the error raised in the remote call and the state of the + * client after that call. + * + * @param client The client returned from {@link #executeErroringCall(AsyncMethodCallback)}. + * @param error The exception raised by the remote call. + */ + abstract void validateError(C client, Exception error); + } + + public void testUnexpectedRemoteExceptionCall() throws Exception { + new ErrorCallTest<Srv.AsyncClient, Boolean>() { + @Override + Srv.AsyncClient executeErroringCall(AsyncMethodCallback<Boolean> callback) throws Exception { + Srv.AsyncClient client = getClient(); + client.declaredExceptionMethod(false, callback); + return client; + } + + @Override + void validateError(Srv.AsyncClient client, Exception error) { + assertFalse(client.hasTimeout()); + assertTrue(error instanceof TException); + } + }.runTest(); + } + + public void testDeclaredRemoteExceptionCall() throws Exception { + new ErrorCallTest<Srv.AsyncClient, Boolean>() { + @Override + Srv.AsyncClient executeErroringCall(AsyncMethodCallback<Boolean> callback) throws Exception { + Srv.AsyncClient client = getClient(); + client.declaredExceptionMethod(true, callback); + return client; + } + + @Override + void validateError(Srv.AsyncClient client, Exception error) { + assertFalse(client.hasTimeout()); + assertEquals(ExceptionWithAMap.class, error.getClass()); + ExceptionWithAMap exceptionWithAMap = (ExceptionWithAMap) error; + assertEquals("blah", exceptionWithAMap.getBlah()); + assertEquals(new HashMap<String, String>(), exceptionWithAMap.getMap_field()); + } + }.runTest(); + } + + public void testTimeoutCall() throws Exception { + new ErrorCallTest<Srv.AsyncClient, Integer>() { + @Override + Srv.AsyncClient executeErroringCall(AsyncMethodCallback<Integer> callback) throws Exception { + Srv.AsyncClient client = getClient(); + client.setTimeout(100); + client.primitiveMethod(callback); + return client; + } + + @Override + void validateError(Srv.AsyncClient client, Exception error) { + assertTrue(client.hasTimeout()); + assertTrue(error instanceof TimeoutException); + } + }.runTest(); + } + + public void testVoidCall() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean returned = new AtomicBoolean(false); + Srv.AsyncClient client = getClient(); + client.voidMethod(new FailureLessCallback<Void>() { + @Override + public void onComplete(Void response) { + returned.set(true); + latch.countDown(); + } + }); + latch.await(1, TimeUnit.SECONDS); + assertTrue(returned.get()); + } + + public void testOnewayCall() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean returned = new AtomicBoolean(false); + Srv.AsyncClient client = getClient(); + client.onewayMethod(new FailureLessCallback<Void>() { + @Override + public void onComplete(Void response) { + returned.set(true); + latch.countDown(); + } + }); + latch.await(1, TimeUnit.SECONDS); + assertTrue(returned.get()); + } + + public void testParallelCalls() throws Exception { + // make multiple calls with deserialization in the selector thread (repro Eric's issue) + int numThreads = 50; + int numCallsPerThread = 100; + List<JankyRunnable> runnables = new ArrayList<JankyRunnable>(); + List<Thread> threads = new ArrayList<Thread>(); + for (int i = 0; i < numThreads; i++) { + JankyRunnable runnable = new JankyRunnable(numCallsPerThread); + Thread thread = new Thread(runnable); + thread.start(); + threads.add(thread); + runnables.add(runnable); + } + for (Thread thread : threads) { + thread.join(); + } + int numSuccesses = 0; + for (JankyRunnable runnable : runnables) { + numSuccesses += runnable.getNumSuccesses(); + } + assertEquals(numThreads * numCallsPerThread, numSuccesses); + } + + private Srv.AsyncClient getClient() throws IOException { + TNonblockingSocket clientSocket = new TNonblockingSocket(ServerTestBase.HOST, ServerTestBase.PORT); + return new Srv.AsyncClient(new TBinaryProtocol.Factory(), clientManager_, clientSocket); + } + + private void basicCall(Srv.AsyncClient client) throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean returned = new AtomicBoolean(false); + client.Janky(1, new FailureLessCallback<Integer>() { + @Override + public void onComplete(Integer response) { + assertEquals(3, response.intValue()); + returned.set(true); + latch.countDown(); + } + + @Override + public void onError(Exception exception) { + try { + StringWriter sink = new StringWriter(); + exception.printStackTrace(new PrintWriter(sink, true)); + fail("unexpected onError with exception " + sink.toString()); + } finally { + latch.countDown(); + } + } + }); + latch.await(100, TimeUnit.SECONDS); + assertTrue(returned.get()); + } + + public class SrvHandler implements Iface { + // Use this method for a standard call testing + @Override + public int Janky(int arg) throws TException { + assertEquals(1, arg); + return 3; + } + + // Using this method for timeout testing - sleeps for 1 second before returning + @Override + public int primitiveMethod() throws TException { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 0; + } + + @Override + public void methodWithDefaultArgs(int something) throws TException { } + + @Override + public CompactProtoTestStruct structMethod() throws TException { + return null; + } + + @Override + public void voidMethod() throws TException { + } + + @Override + public void onewayMethod() throws TException { + } + + @Override + public boolean declaredExceptionMethod(boolean shouldThrowDeclared) throws TException { + if (shouldThrowDeclared) { + throw new ExceptionWithAMap("blah", new HashMap<String, String>()); + } else { + throw new TException("Unexpected!"); + } + } + } + + private static abstract class FailureLessCallback<T> implements AsyncMethodCallback<T> { + @Override + public void onError(Exception exception) { + fail(exception); + } + } + + private static void fail(Exception exception) { + StringWriter sink = new StringWriter(); + exception.printStackTrace(new PrintWriter(sink, true)); + fail("unexpected error " + sink.toString()); + } + + private class JankyRunnable implements Runnable { + private int numCalls_; + private int numSuccesses_ = 0; + private Srv.AsyncClient client_; + + public JankyRunnable(int numCalls) throws Exception { + numCalls_ = numCalls; + client_ = getClient(); + client_.setTimeout(20000); + } + + public int getNumSuccesses() { + return numSuccesses_; + } + + public void run() { + for (int i = 0; i < numCalls_ && !client_.hasError(); i++) { + final int iteration = i; + try { + // connect an async client + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean returned = new AtomicBoolean(false); + client_.Janky(1, new AsyncMethodCallback<Integer>() { + + @Override + public void onComplete(Integer result) { + assertEquals(3, result.intValue()); + returned.set(true); + latch.countDown(); + } + + @Override + public void onError(Exception exception) { + try { + StringWriter sink = new StringWriter(); + exception.printStackTrace(new PrintWriter(sink, true)); + fail("unexpected onError on iteration " + iteration + ": " + sink.toString()); + } finally { + latch.countDown(); + } + } + }); + + boolean calledBack = latch.await(30, TimeUnit.SECONDS); + assertTrue("wasn't called back in time on iteration " + iteration, calledBack); + assertTrue("onComplete not called on iteration " + iteration, returned.get()); + this.numSuccesses_++; + } catch (Exception e) { + fail(e); + } + } + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java new file mode 100644 index 000000000..e88160759 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/BenchmarkProtocols.java @@ -0,0 +1,88 @@ +package org.apache.thrift.protocol; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.TException; +import org.apache.thrift.transport.TMemoryBuffer; + +public class BenchmarkProtocols { + + private static final Set<TProtocolFactory> FACTORIES = new LinkedHashSet<TProtocolFactory>(){{ + add(new TTupleProtocol.Factory()); + add(new TCompactProtocol.Factory()); + add(new TBinaryProtocol.Factory()); + }}; + + private static final int NUM_REPS = 100000; + private static final int NUM_TRIALS = 10; + + public static void main(String[] args) throws TException { + Map<TProtocolFactory, List<Long>> timesByFactory = new HashMap<TProtocolFactory, List<Long>>(); + + for (int trial = 0; trial < NUM_TRIALS; trial++) { + for (int i = 0; i < 16; i++) { + System.gc(); + } +// TProtocol proto = factory.getProtocol(new TTransport() { +// @Override +// public void write(byte[] buf, int off, int len) throws TTransportException { +// } +// +// @Override +// public int read(byte[] buf, int off, int len) throws TTransportException { +// return 0; +// } +// +// @Override +// public void open() throws TTransportException { +// } +// +// @Override +// public boolean isOpen() { +// return true; +// } +// +// @Override +// public void close() { +// } +// }); + + + for (TProtocolFactory factory : FACTORIES) { + if (timesByFactory.get(factory) == null) { + timesByFactory.put(factory, new ArrayList<Long>()); + } + + long start = System.currentTimeMillis(); + for (int rep = 0; rep < NUM_REPS; rep++) { + TProtocol proto = factory.getProtocol(new TMemoryBuffer(128*1024)); + Fixtures.compactProtoTestStruct.write(proto); + Fixtures.nesting.write(proto); + } + long end = System.currentTimeMillis(); + timesByFactory.get(factory).add(end-start); + } + } + + for (TProtocolFactory factory : FACTORIES) { + List<Long> times = timesByFactory.get(factory); +// System.out.println("raw times pre-drop: " + times ); + times.remove(Collections.max(times)); + long total = 0; + for (long t : times) { + total += t; + } + Collections.sort(times); + System.out.println(factory.getClass().getName() + " average time: " + (total / times.size()) + "ms"); + System.out.println("raw times: " + times); + } + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java new file mode 100644 index 000000000..0386d8393 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/ProtocolTestBase.java @@ -0,0 +1,427 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.TBase; +import org.apache.thrift.TDeserializer; +import org.apache.thrift.TException; +import org.apache.thrift.TSerializer; +import org.apache.thrift.transport.TMemoryBuffer; + +import thrift.test.CompactProtoTestStruct; +import thrift.test.HolyMoley; +import thrift.test.Nesting; +import thrift.test.OneOfEach; +import thrift.test.Srv; + +public abstract class ProtocolTestBase extends TestCase { + + /** Does it make sense to call methods like writeI32 directly on your protocol? */ + protected abstract boolean canBeUsedNaked(); + + /** The protocol factory for the protocol being tested. */ + protected abstract TProtocolFactory getFactory(); + + public void testDouble() throws Exception { + if (canBeUsedNaked()) { + TMemoryBuffer buf = new TMemoryBuffer(1000); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeDouble(123.456); + assertEquals(123.456, proto.readDouble()); + } + + internalTestStructField(new StructFieldTestCase(TType.DOUBLE, (short)15) { + @Override + public void readMethod(TProtocol proto) throws TException { + assertEquals(123.456, proto.readDouble()); + } + + @Override + public void writeMethod(TProtocol proto) throws TException { + proto.writeDouble(123.456); + } + }); + } + + public void testSerialization() throws Exception { + internalTestSerialization(OneOfEach.class, Fixtures.oneOfEach); + internalTestSerialization(Nesting.class, Fixtures.nesting); + internalTestSerialization(HolyMoley.class, Fixtures.holyMoley); + internalTestSerialization(CompactProtoTestStruct.class, Fixtures.compactProtoTestStruct); + } + + public void testBinary() throws Exception { + for (byte[] b : Arrays.asList(new byte[0], + new byte[]{0,1,2,3,4,5,6,7,8,9,10}, + new byte[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}, + new byte[]{0x5D}, + new byte[]{(byte)0xD5,(byte)0x5D}, + new byte[]{(byte)0xFF,(byte)0xD5,(byte)0x5D}, + new byte[128])) { + if (canBeUsedNaked()) { + internalTestNakedBinary(b); + } + internalTestBinaryField(b); + } + + if (canBeUsedNaked()) { + byte[] data = {1, 2, 3, 4, 5, 6}; + + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + ByteBuffer bb = ByteBuffer.wrap(data); + bb.get(); + proto.writeBinary(bb.slice()); + assertEquals(ByteBuffer.wrap(data, 1, 5), proto.readBinary()); + } + } + + public void testString() throws Exception { + for (String s : Arrays.asList("", "short", "borderlinetiny", "a bit longer than the smallest possible")) { + if (canBeUsedNaked()) { + internalTestNakedString(s); + } + internalTestStringField(s); + } + } + + public void testLong() throws Exception { + if (canBeUsedNaked()) { + internalTestNakedI64(0); + } + internalTestI64Field(0); + for (int i = 0; i < 62; i++) { + if (canBeUsedNaked()) { + internalTestNakedI64(1L << i); + internalTestNakedI64(-(1L << i)); + } + internalTestI64Field(1L << i); + internalTestI64Field(-(1L << i)); + } + } + + public void testInt() throws Exception { + for (int i : Arrays.asList(0, 1, 7, 150, 15000, 31337, 0xffff, 0xffffff, -1, -7, -150, -15000, -0xffff, -0xffffff)) { + if (canBeUsedNaked()) { + internalTestNakedI32(i); + } + internalTestI32Field(i); + } + } + + public void testShort() throws Exception { + for (int s : Arrays.asList(0, 1, 7, 150, 15000, 0x7fff, -1, -7, -150, -15000, -0x7fff)) { + if (canBeUsedNaked()) { + internalTestNakedI16((short)s); + } + internalTestI16Field((short)s); + } + } + + public void testByte() throws Exception { + if (canBeUsedNaked()) { + internalTestNakedByte(); + } + for (int i = 0; i < 128; i++) { + internalTestByteField((byte)i); + internalTestByteField((byte)-i); + } + } + + private void internalTestNakedByte() throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(1000); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeByte((byte)123); + assertEquals((byte) 123, proto.readByte()); + } + + private void internalTestByteField(final byte b) throws Exception { + internalTestStructField(new StructFieldTestCase(TType.BYTE, (short)15) { + public void writeMethod(TProtocol proto) throws TException { + proto.writeByte(b); + } + + public void readMethod(TProtocol proto) throws TException { + assertEquals((byte)b, proto.readByte()); + } + }); + } + + private void internalTestNakedI16(short n) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeI16(n); + assertEquals(n, proto.readI16()); + } + + private void internalTestI16Field(final short n) throws Exception { + internalTestStructField(new StructFieldTestCase(TType.I16, (short)15) { + public void writeMethod(TProtocol proto) throws TException { + proto.writeI16(n); + } + + public void readMethod(TProtocol proto) throws TException { + assertEquals(n, proto.readI16()); + } + }); + } + + private void internalTestNakedI32(int n) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeI32(n); + assertEquals(n, proto.readI32()); + } + + private void internalTestI32Field(final int n) throws Exception { + internalTestStructField(new StructFieldTestCase(TType.I32, (short)15) { + public void writeMethod(TProtocol proto) throws TException { + proto.writeI32(n); + } + + public void readMethod(TProtocol proto) throws TException { + assertEquals(n, proto.readI32()); + } + }); + } + + private void internalTestNakedI64(long n) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeI64(n); + assertEquals(n, proto.readI64()); + } + + private void internalTestI64Field(final long n) throws Exception { + internalTestStructField(new StructFieldTestCase(TType.I64, (short)15) { + public void writeMethod(TProtocol proto) throws TException { + proto.writeI64(n); + } + + public void readMethod(TProtocol proto) throws TException { + assertEquals(n, proto.readI64()); + } + }); + } + + private void internalTestNakedString(String str) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeString(str); + assertEquals(str, proto.readString()); + } + + private void internalTestStringField(final String str) throws Exception { + internalTestStructField(new StructFieldTestCase(TType.STRING, (short)15) { + public void writeMethod(TProtocol proto) throws TException { + proto.writeString(str); + } + + public void readMethod(TProtocol proto) throws TException { + assertEquals(str, proto.readString()); + } + }); + } + + private void internalTestNakedBinary(byte[] data) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + proto.writeBinary(ByteBuffer.wrap(data)); + assertEquals(ByteBuffer.wrap(data), proto.readBinary()); + } + + private void internalTestBinaryField(final byte[] data) throws Exception { + internalTestStructField(new StructFieldTestCase(TType.STRING, (short)15) { + public void writeMethod(TProtocol proto) throws TException { + proto.writeBinary(ByteBuffer.wrap(data)); + } + + public void readMethod(TProtocol proto) throws TException { + assertEquals(ByteBuffer.wrap(data), proto.readBinary()); + } + }); + } + + private <T extends TBase> void internalTestSerialization(Class<T> klass, T expected) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TBinaryProtocol binproto = new TBinaryProtocol(buf); + + expected.write(binproto); + + buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + + expected.write(proto); + System.out.println("Size in " + proto.getClass().getSimpleName() + ": " + buf.length()); + + T actual = klass.newInstance(); + actual.read(proto); + assertEquals(expected, actual); + } + + public void testMessage() throws Exception { + List<TMessage> msgs = Arrays.asList(new TMessage[]{ + new TMessage("short message name", TMessageType.CALL, 0), + new TMessage("1", TMessageType.REPLY, 12345), + new TMessage("loooooooooooooooooooooooooooooooooong", TMessageType.EXCEPTION, 1 << 16), + new TMessage("Janky", TMessageType.CALL, 0), + }); + + for (TMessage msg : msgs) { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + TMessage output = null; + + proto.writeMessageBegin(msg); + proto.writeMessageEnd(); + + output = proto.readMessageBegin(); + + assertEquals(msg, output); + } + } + + public void testServerRequest() throws Exception { + Srv.Iface handler = new Srv.Iface() { + public int Janky(int i32arg) throws TException { + return i32arg * 2; + } + + public int primitiveMethod() throws TException { + return 0; + } + + public CompactProtoTestStruct structMethod() throws TException { + return null; + } + + public void voidMethod() throws TException { + } + + public void methodWithDefaultArgs(int something) throws TException { + } + + @Override + public void onewayMethod() throws TException { + } + + @Override + public boolean declaredExceptionMethod(boolean shouldThrow) throws TException { + return shouldThrow; + } + }; + + Srv.Processor testProcessor = new Srv.Processor(handler); + + TMemoryBuffer clientOutTrans = new TMemoryBuffer(0); + TProtocol clientOutProto = getFactory().getProtocol(clientOutTrans); + TMemoryBuffer clientInTrans = new TMemoryBuffer(0); + TProtocol clientInProto = getFactory().getProtocol(clientInTrans); + + Srv.Client testClient = new Srv.Client(clientInProto, clientOutProto); + + testClient.send_Janky(1); + // System.out.println(clientOutTrans.inspect()); + testProcessor.process(clientOutProto, clientInProto); + // System.out.println(clientInTrans.inspect()); + assertEquals(2, testClient.recv_Janky()); + } + + public void testTDeserializer() throws TException { + TSerializer ser = new TSerializer(getFactory()); + byte[] bytes = ser.serialize(Fixtures.compactProtoTestStruct); + + TDeserializer deser = new TDeserializer(getFactory()); + CompactProtoTestStruct cpts = new CompactProtoTestStruct(); + deser.deserialize(cpts, bytes); + + assertEquals(Fixtures.compactProtoTestStruct, cpts); + } + + // + // Helper methods + // + + private void internalTestStructField(StructFieldTestCase testCase) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + TProtocol proto = getFactory().getProtocol(buf); + + TField field = new TField("test_field", testCase.type_, testCase.id_); + proto.writeStructBegin(new TStruct("test_struct")); + proto.writeFieldBegin(field); + testCase.writeMethod(proto); + proto.writeFieldEnd(); + proto.writeStructEnd(); + + proto.readStructBegin(); + TField readField = proto.readFieldBegin(); + assertEquals(testCase.id_, readField.id); + assertEquals(testCase.type_, readField.type); + testCase.readMethod(proto); + proto.readStructEnd(); + } + + private static abstract class StructFieldTestCase { + byte type_; + short id_; + public StructFieldTestCase(byte type, short id) { + type_ = type; + id_ = id; + } + + public abstract void writeMethod(TProtocol proto) throws TException; + public abstract void readMethod(TProtocol proto) throws TException; + } + + private static final int NUM_TRIALS = 5; + private static final int NUM_REPS = 10000; + + protected void benchmark() throws Exception { + for (int trial = 0; trial < NUM_TRIALS; trial++) { + TSerializer ser = new TSerializer(getFactory()); + byte[] serialized = null; + long serStart = System.currentTimeMillis(); + for (int rep = 0; rep < NUM_REPS; rep++) { + serialized = ser.serialize(Fixtures.holyMoley); + } + long serEnd = System.currentTimeMillis(); + long serElapsed = serEnd - serStart; + System.out.println("Ser:\t" + serElapsed + "ms\t" + + ((double)serElapsed / NUM_REPS) + "ms per serialization"); + + HolyMoley cpts = new HolyMoley(); + TDeserializer deser = new TDeserializer(getFactory()); + long deserStart = System.currentTimeMillis(); + for (int rep = 0; rep < NUM_REPS; rep++) { + deser.deserialize(cpts, serialized); + } + long deserEnd = System.currentTimeMillis(); + long deserElapsed = deserEnd - deserStart; + System.out.println("Des:\t" + deserElapsed + "ms\t" + + ((double)deserElapsed / NUM_REPS) + "ms per deserialization"); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java new file mode 100644 index 000000000..c8e78eee6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestShortStack.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import junit.framework.TestCase; + +public class TestShortStack extends TestCase { + + public void testOps() throws Exception { + ShortStack s = new ShortStack(1); + s.push((short)10); + s.push((short)11); + s.push((short)12); + assertEquals((short)12, s.pop()); + assertEquals((short)11, s.pop()); + s.push((short)40); + assertEquals((short)40, s.pop()); + assertEquals((short)10, s.pop()); + try { + s.pop(); + fail("should have thrown an exception!"); + } catch (Exception e) { + // yay + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java new file mode 100644 index 000000000..b4c0888a4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTCompactProtocol.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TDeserializer; +import org.apache.thrift.TException; + +import thrift.test.Bonk; + +public class TestTCompactProtocol extends ProtocolTestBase { + @Override + protected TProtocolFactory getFactory() { + return new TCompactProtocol.Factory(); + } + + @Override + protected boolean canBeUsedNaked() { + return true; + } + + public void testOOMDenialOfService() throws Exception { + // Struct header, Integer.MAX_VALUE length, and only one real + // byte of data + byte [] bytes = {24, -1, -1, -1, -17, 49}; + TDeserializer deser = new TDeserializer(new TCompactProtocol + .Factory(1000)); + Bonk bonk = new Bonk(); + try { + deser.deserialize(bonk, bytes); + } catch (TException e) { + // Ignore as we are only checking for OOM in the failure case + } + } + + public static void main(String args[]) throws Exception { + new TestTCompactProtocol().benchmark(); + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java new file mode 100644 index 000000000..f72c25972 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTField.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import junit.framework.TestCase; +import static org.junit.Assert.assertNotEquals; + +public abstract class TestTField extends TestCase { + + public void testConstructor() { + TField uut = new TField(); + assertEquals("", uut.name); + assertEquals(TType.STOP, uut.type); + assertEquals(0, uut.id); + + uut = new TField("foo", TType.VOID, (short)42); + assertEquals("foo", uut.name); + assertEquals(TType.VOID, uut.type); + assertEquals(42, uut.id); + } + + public void testEquality() { + TField uut1 = new TField(); + TField uut2 = new TField(); + assertEquals(uut1, uut2); + assertEquals(uut1.hashCode(), uut2.hashCode()); + + uut1 = new TField("foo", TType.I32, (short)1); + uut2 = new TField("foo", TType.I32, (short)2); + assertNotEquals(uut1, uut2); + assertNotEquals(uut1.hashCode(), uut2.hashCode()); + + uut1 = new TField("foo", TType.VOID, (short)1); + uut2 = new TField("foo", TType.I32, (short)1); + assertNotEquals(uut1, uut2); + assertNotEquals(uut1.hashCode(), uut2.hashCode()); + + uut1 = new TField("foo", TType.VOID, (short)5); + uut2 = new TField("bar", TType.I32, (short)5); + assertEquals(uut1, uut2); // name field is ignored + assertEquals(uut1.hashCode(), uut2.hashCode()); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java new file mode 100644 index 000000000..c2ca1fa7a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTJSONProtocol.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.nio.charset.StandardCharsets; + +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.transport.TMemoryBuffer; + +public class TestTJSONProtocol extends ProtocolTestBase { + @Override + protected TProtocolFactory getFactory() { + return new TJSONProtocol.Factory(); + } + + @Override + protected boolean canBeUsedNaked() { + return false; + } + + public void testEscapedUnicode() throws TException { + String jsonString = "\"hello unicode \\u0e01\\ud834\\udd1e world\""; + String expectedString = "hello unicode \u0e01\ud834\udd1e world"; + + TMemoryBuffer buffer = new TMemoryBuffer(1000); + TJSONProtocol protocol = new TJSONProtocol(buffer); + buffer.write(jsonString.getBytes(StandardCharsets.UTF_8)); + + assertEquals(expectedString, protocol.readString()); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java new file mode 100644 index 000000000..89cf5366e --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTProtocolUtil.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import junit.framework.TestCase; + +import org.apache.thrift.TSerializer; + +import thrift.test.GuessProtocolStruct; + +public class TestTProtocolUtil extends TestCase { + + public void testGuessProtocolFactory_JSON() throws Exception { + + byte[] data = "{foo}".getBytes(); + TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(data, new TCompactProtocol.Factory()); + assertTrue(factory instanceof TJSONProtocol.Factory); + + // Make sure data serialized with TCompact and which starts with '{' + // is not mistakenly guessed as serialized with JSON. + + GuessProtocolStruct s = new GuessProtocolStruct(); + s.putToMap_field("}","}"); + byte[] ser = new TSerializer(new TCompactProtocol.Factory()).serialize(s); + factory = TProtocolUtil.guessProtocolFactory(ser, new TCompactProtocol.Factory()); + assertFalse(factory instanceof TJSONProtocol.Factory); + } + + public void testGuessProtocolFactory_Binary() throws Exception { + // Check that a last byte != 0 is correctly reported as Binary + + byte[] buf = new byte[1]; + for (int i = 1; i < 256; i++) { + buf[0] = (byte) i; + TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TCompactProtocol.Factory()); + assertTrue(factory instanceof TBinaryProtocol.Factory); + } + + // Check that a second byte set to 0 is reported as Binary + buf = new byte[2]; + TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TCompactProtocol.Factory()); + assertTrue(factory instanceof TBinaryProtocol.Factory); + } + + public void testGuessProtocolFactory_Compact() throws Exception { + // Check that a first byte > 0x10 is reported as Compact + byte[] buf = new byte[3]; + buf[0] = 0x11; + TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TBinaryProtocol.Factory()); + assertTrue(factory instanceof TCompactProtocol.Factory); + + // Check that second byte >= 0x80 is reported as Compact + buf[0] = 0; + for (int i = 0x80; i < 0x100; i++) { + buf[1] = (byte) i; + factory = TProtocolUtil.guessProtocolFactory(buf, new TBinaryProtocol.Factory()); + assertTrue(factory instanceof TCompactProtocol.Factory); + } + } + + public void testGuessProtocolFactory_Undecided() throws Exception { + byte[] buf = new byte[3]; + buf[1] = 0x7e; + TProtocolFactory factory = TProtocolUtil.guessProtocolFactory(buf, new TSimpleJSONProtocol.Factory()); + assertTrue(factory instanceof TSimpleJSONProtocol.Factory); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java new file mode 100644 index 000000000..171a487ea --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTSimpleJSONProtocol.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.nio.charset.StandardCharsets; + +import junit.framework.TestCase; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.TException; +import org.apache.thrift.transport.TMemoryBuffer; + +import thrift.test.CompactProtoTestStruct; +import thrift.test.HolyMoley; + +public class TestTSimpleJSONProtocol extends TestCase { + private TMemoryBuffer buf; + private TSimpleJSONProtocol proto; + + @Override + protected void setUp() throws Exception { + buf = new TMemoryBuffer(1000); + proto = new TSimpleJSONProtocol(buf); + } + + private String bufToString() { + return buf.toString(StandardCharsets.UTF_8); + } + + public void testHolyMoley() throws TException { + final HolyMoley holyMoley = Fixtures.holyMoley.deepCopy(); + // unset sets that produce inconsistent ordering between JDK7/8 + holyMoley.unsetBonks(); + holyMoley.unsetContain(); + holyMoley.write(proto); + assertEquals("{\"big\":[{\"im_true\":1,\"im_false\":0,\"a_bite\":35,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]},{\"im_true\":1,\"im_false\":0,\"a_bite\":-42,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]}]}", bufToString()); + } + + public void testNesting() throws TException { + Fixtures.nesting.write(proto); + assertEquals("{\"my_bonk\":{\"type\":31337,\"message\":\"I am a bonk... xor!\"},\"my_ooe\":{\"im_true\":1,\"im_false\":0,\"a_bite\":-42,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]}}", bufToString()); + } + + public void testOneOfEach() throws TException { + Fixtures.oneOfEach.write(proto); + assertEquals("{\"im_true\":1,\"im_false\":0,\"a_bite\":-42,\"integer16\":27000,\"integer32\":16777216,\"integer64\":6000000000,\"double_precision\":3.141592653589793,\"some_characters\":\"JSON THIS! \\\"\\u0001\",\"zomg_unicode\":\"ӀⅮΝ Нοⅿоɡгаρℎ Αttαⅽκ�‼\",\"what_who\":0,\"base64\":\"base64\",\"byte_list\":[1,2,3],\"i16_list\":[1,2,3],\"i64_list\":[1,2,3]}", bufToString()); + } + + public void testSanePartsOfCompactProtoTestStruct() throws TException { + // unset all the maps with container keys + CompactProtoTestStruct struct = Fixtures.compactProtoTestStruct.deepCopy(); + struct.unsetList_byte_map(); + struct.unsetSet_byte_map(); + struct.unsetMap_byte_map(); + // unset sets and maps that produce inconsistent ordering between JDK7/8 + struct.unsetByte_set(); + struct.unsetI16_set(); + struct.unsetI64_set(); + struct.unsetDouble_set(); + struct.unsetString_set(); + struct.unsetI16_byte_map(); + struct.unsetI32_byte_map(); + struct.unsetI64_byte_map(); + struct.unsetDouble_byte_map(); + struct.unsetString_byte_map(); + struct.write(proto); + assertEquals("{\"a_byte\":127,\"a_i16\":32000,\"a_i32\":1000000000,\"a_i64\":1099511627775,\"a_double\":5.6789,\"a_string\":\"my string\",\"a_binary\":\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\",\"true_field\":1,\"false_field\":0,\"empty_struct_field\":{},\"byte_list\":[-127,-1,0,1,127],\"i16_list\":[-1,0,1,32767],\"i32_list\":[-1,0,255,65535,16777215,2147483647],\"i64_list\":[-1,0,255,65535,16777215,4294967295,1099511627775,281474976710655,72057594037927935,9223372036854775807],\"double_list\":[0.1,0.2,0.3],\"string_list\":[\"first\",\"second\",\"third\"],\"boolean_list\":[1,1,1,0,0,0],\"struct_list\":[{},{}],\"i32_set\":[1,2,3],\"boolean_set\":[0,1],\"struct_set\":[{}],\"byte_byte_map\":{\"1\":2},\"boolean_byte_map\":{\"0\":0,\"1\":1},\"byte_i16_map\":{\"1\":1,\"2\":-1,\"3\":32767},\"byte_i32_map\":{\"1\":1,\"2\":-1,\"3\":2147483647},\"byte_i64_map\":{\"1\":1,\"2\":-1,\"3\":9223372036854775807},\"byte_double_map\":{\"1\":0.1,\"2\":-0.1,\"3\":1000000.1},\"byte_string_map\":{\"1\":\"\",\"2\":\"blah\",\"3\":\"loooooooooooooong string\"},\"byte_boolean_map\":{\"1\":1,\"2\":0},\"byte_map_map\":{\"0\":{},\"1\":{\"1\":1},\"2\":{\"1\":1,\"2\":2}},\"byte_set_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"byte_list_map\":{\"0\":[],\"1\":[1],\"2\":[1,2]},\"field500\":500,\"field5000\":5000,\"field20000\":20000}", bufToString()); + } + + public void testThrowsOnCollectionKeys() throws TException { + try { + Fixtures.compactProtoTestStruct.write(proto); + fail("this should throw a CollectionMapKeyException"); + } catch (TSimpleJSONProtocol.CollectionMapKeyException e) { + // + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java new file mode 100644 index 000000000..b654db3f8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/protocol/TestTTupleProtocol.java @@ -0,0 +1,27 @@ +package org.apache.thrift.protocol; + +import org.apache.thrift.TDeserializer; +import org.apache.thrift.TSerializer; + +import thrift.test.TupleProtocolTestStruct; + + +public class TestTTupleProtocol extends ProtocolTestBase { + + @Override + protected boolean canBeUsedNaked() { + return false; + } + + @Override + protected TProtocolFactory getFactory() { + return new TTupleProtocol.Factory(); + } + + public void testBitsetLengthIssue() throws Exception { + final TupleProtocolTestStruct t1 = new TupleProtocolTestStruct(); + t1.setField1(0); + t1.setField2(12); + new TDeserializer(new TTupleProtocol.Factory()).deserialize(new TupleProtocolTestStruct(), new TSerializer(new TTupleProtocol.Factory()).serialize(t1)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java new file mode 100644 index 000000000..33f229e88 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/scheme/TestStandardScheme.java @@ -0,0 +1,40 @@ +package org.apache.thrift.scheme; + +import junit.framework.TestCase; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.TBase; +import org.apache.thrift.TDeserializer; +import org.apache.thrift.TException; +import org.apache.thrift.TSerializer; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TMemoryBuffer; +import org.apache.thrift.transport.TTransport; + +import thrift.test.HolyMoley; +import thrift.test.Nesting; +import thrift.test.OneOfEach; + +public class TestStandardScheme extends TestCase { + TSerializer serializer = new TSerializer(); + TDeserializer deserializer = new TDeserializer(); + + /** + * This tests whether the Standard Scheme properly reads structs serialized + * using an older version of thrift. + */ + public void testPersistentStructs() throws TException { + readAndCompare(new OneOfEach(), Fixtures.oneOfEach, Fixtures.persistentBytesOneOfEach); + readAndCompare(new HolyMoley(), Fixtures.holyMoley, Fixtures.persistentBytesHolyMoley); + readAndCompare(new Nesting(), Fixtures.nesting, Fixtures.persistentBytesNesting); + } + + public void readAndCompare(TBase struct, TBase fixture, byte[] inputBytes) throws TException { + TTransport trans = new TMemoryBuffer(0); + trans.write(inputBytes, 0, inputBytes.length); + TProtocol iprot = new TBinaryProtocol(trans); + struct.read(iprot); + assertEquals(fixture, struct); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java new file mode 100644 index 000000000..8348cbc3d --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/ServerTestBase.java @@ -0,0 +1,715 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import java.nio.ByteBuffer; + +import junit.framework.TestCase; + +import org.apache.thrift.TException; +import org.apache.thrift.TProcessor; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportFactory; +import org.apache.thrift.transport.TFramedTransport.Factory; + +import thrift.test.Insanity; +import thrift.test.Numberz; +import thrift.test.ThriftTest; +import thrift.test.Xception; +import thrift.test.Xception2; +import thrift.test.Xtruct; +import thrift.test.Xtruct2; + +public abstract class ServerTestBase extends TestCase { + + public static class TestHandler implements ThriftTest.Iface { + + public TestHandler() {} + + public void testVoid() { + System.out.print("testVoid()\n"); + } + + public String testString(String thing) { + System.out.print("testString(\"" + thing + "\")\n"); + return thing; + } + + public boolean testBool(boolean thing) { + System.out.print("testBool(" + thing + ")\n"); + return thing; + } + + public byte testByte(byte thing) { + System.out.print("testByte(" + thing + ")\n"); + return thing; + } + + public int testI32(int thing) { + System.out.print("testI32(" + thing + ")\n"); + return thing; + } + + public long testI64(long thing) { + System.out.print("testI64(" + thing + ")\n"); + return thing; + } + + public double testDouble(double thing) { + System.out.print("testDouble(" + thing + ")\n"); + return thing; + } + + public ByteBuffer testBinary(ByteBuffer thing) { + StringBuilder sb = new StringBuilder(thing.remaining() * 3); + thing.mark(); + int limit = 0; // limit output to keep the log size sane + while ((thing.remaining() > 0) && (++limit < 1024)) { + sb.append(String.format("%02X ", thing.get())); + } + if(thing.remaining() > 0) { + sb.append("..."); // indicate we have more date + } + System.out.print("testBinary(" + sb.toString() + ")\n"); + thing.reset(); + return thing; + } + + public Xtruct testStruct(Xtruct thing) { + System.out.print("testStruct({" + + "\"" + thing.string_thing + "\", " + + thing.byte_thing + ", " + + thing.i32_thing + ", " + + thing.i64_thing + "})\n"); + return thing; + } + + public Xtruct2 testNest(Xtruct2 nest) { + Xtruct thing = nest.struct_thing; + System.out.print("testNest({" + + nest.byte_thing + ", {" + + "\"" + thing.string_thing + "\", " + + thing.byte_thing + ", " + + thing.i32_thing + ", " + + thing.i64_thing + "}, " + + nest.i32_thing + "})\n"); + return nest; + } + + public Map<Integer,Integer> testMap(Map<Integer,Integer> thing) { + System.out.print("testMap({"); + System.out.print(thing); + System.out.print("})\n"); + return thing; + } + + public Map<String,String> testStringMap(Map<String,String> thing) { + System.out.print("testStringMap({"); + System.out.print(thing); + System.out.print("})\n"); + return thing; + } + + public Set<Integer> testSet(Set<Integer> thing) { + System.out.print("testSet({"); + boolean first = true; + for (int elem : thing) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(elem); + } + System.out.print("})\n"); + return thing; + } + + public List<Integer> testList(List<Integer> thing) { + System.out.print("testList({"); + boolean first = true; + for (int elem : thing) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(elem); + } + System.out.print("})\n"); + return thing; + } + + public Numberz testEnum(Numberz thing) { + System.out.print("testEnum(" + thing + ")\n"); + return thing; + } + + public long testTypedef(long thing) { + System.out.print("testTypedef(" + thing + ")\n"); + return thing; + } + + public Map<Integer,Map<Integer,Integer>> testMapMap(int hello) { + System.out.print("testMapMap(" + hello + ")\n"); + Map<Integer,Map<Integer,Integer>> mapmap = + new HashMap<Integer,Map<Integer,Integer>>(); + + HashMap<Integer,Integer> pos = new HashMap<Integer,Integer>(); + HashMap<Integer,Integer> neg = new HashMap<Integer,Integer>(); + for (int i = 1; i < 5; i++) { + pos.put(i, i); + neg.put(-i, -i); + } + + mapmap.put(4, pos); + mapmap.put(-4, neg); + + return mapmap; + } + + public Map<Long, Map<Numberz,Insanity>> testInsanity(Insanity argument) { + System.out.print("testInsanity()\n"); + + HashMap<Numberz,Insanity> first_map = new HashMap<Numberz, Insanity>(); + HashMap<Numberz,Insanity> second_map = new HashMap<Numberz, Insanity>();; + + first_map.put(Numberz.TWO, argument); + first_map.put(Numberz.THREE, argument); + + Insanity looney = new Insanity(); + second_map.put(Numberz.SIX, looney); + + Map<Long,Map<Numberz,Insanity>> insane = + new HashMap<Long, Map<Numberz,Insanity>>(); + insane.put((long)1, first_map); + insane.put((long)2, second_map); + + return insane; + } + + public Xtruct testMulti(byte arg0, int arg1, long arg2, Map<Short,String> arg3, Numberz arg4, long arg5) { + System.out.print("testMulti()\n"); + + Xtruct hello = new Xtruct();; + hello.string_thing = "Hello2"; + hello.byte_thing = arg0; + hello.i32_thing = arg1; + hello.i64_thing = arg2; + return hello; + } + + public void testException(String arg) throws Xception, TException { + System.out.print("testException("+arg+")\n"); + if ("Xception".equals(arg)) { + Xception x = new Xception(); + x.errorCode = 1001; + x.message = arg; + throw x; + } else if ("TException".equals(arg)) { + // Unspecified exception should yield a TApplicationException on client side + throw new RuntimeException(arg); + } else { + Xtruct result = new Xtruct(); + result.string_thing = arg; + } + return; + } + + public Xtruct testMultiException(String arg0, String arg1) throws Xception, Xception2 { + System.out.print("testMultiException(" + arg0 + ", " + arg1 + ")\n"); + if (arg0.equals("Xception")) { + Xception x = new Xception(); + x.errorCode = 1001; + x.message = "This is an Xception"; + throw x; + } else if (arg0.equals("Xception2")) { + Xception2 x = new Xception2(); + x.errorCode = 2002; + x.struct_thing = new Xtruct(); + x.struct_thing.string_thing = "This is an Xception2"; + throw x; + } + + Xtruct result = new Xtruct(); + result.string_thing = arg1; + return result; + } + + public void testOneway(int sleepFor) { + System.out.println("testOneway(" + Integer.toString(sleepFor) + + ") => sleeping..."); + try { + Thread.sleep(sleepFor * SLEEP_DELAY); + System.out.println("Done sleeping!"); + } catch (InterruptedException ie) { + throw new RuntimeException(ie); + } + } + } // class TestHandler + + private static final List<TProtocolFactory> PROTOCOLS = Arrays.asList( + new TBinaryProtocol.Factory(), + new TCompactProtocol.Factory()); + + public static final String HOST = "localhost"; + public static final int PORT = Integer.valueOf( + System.getProperty("test.port", "9090")); + protected static final int SLEEP_DELAY = 1000; + protected static final int SOCKET_TIMEOUT = 1500; + private static final Xtruct XSTRUCT = new Xtruct("Zero", (byte) 1, -3, -5); + private static final Xtruct2 XSTRUCT2 = new Xtruct2((byte)1, XSTRUCT, 5); + + public void startServer(TProcessor processor, TProtocolFactory protoFactory) throws Exception{ + startServer(processor, protoFactory, null); + } + + public abstract void startServer(TProcessor processor, TProtocolFactory protoFactory, TTransportFactory factory) throws Exception; + + public abstract void stopServer() throws Exception; + + public abstract TTransport getClientTransport(TTransport underlyingTransport) throws Exception; + + private void testBool(ThriftTest.Client testClient) throws TException { + boolean t = testClient.testBool(true); + assertEquals(true, t); + boolean f = testClient.testBool(false); + assertEquals(false, f); + } + + private void testByte(ThriftTest.Client testClient) throws TException { + byte i8 = testClient.testByte((byte)1); + assertEquals(1, i8); + } + + private void testDouble(ThriftTest.Client testClient) throws TException { + double dub = testClient.testDouble(5.325098235); + assertEquals(5.325098235, dub); + } + + private void testEnum(ThriftTest.Client testClient) throws TException { + assertEquals(Numberz.ONE, testClient.testEnum(Numberz.ONE)); + assertEquals(Numberz.TWO, testClient.testEnum(Numberz.TWO)); + assertEquals(Numberz.THREE, testClient.testEnum(Numberz.THREE)); + assertEquals(Numberz.FIVE, testClient.testEnum(Numberz.FIVE)); + assertEquals(Numberz.EIGHT, testClient.testEnum(Numberz.EIGHT)); + } + + private void testI32(ThriftTest.Client testClient) throws TException { + int i32 = testClient.testI32(-1); + assertEquals(i32, -1); + } + + private void testI64(ThriftTest.Client testClient) throws TException { + long i64 = testClient.testI64(-34359738368L); + assertEquals(i64, -34359738368L); + } + + // todo: add assertions + private void testInsanity(ThriftTest.Client testClient) throws TException { + Insanity insane; + + insane = new Insanity(); + insane.userMap = new HashMap<Numberz, Long>(); + insane.userMap.put(Numberz.FIVE, (long)5000); + Xtruct truck = new Xtruct(); + truck.string_thing = "Truck"; + truck.byte_thing = (byte)8; + truck.i32_thing = 8; + truck.i64_thing = 8; + insane.xtructs = new ArrayList<Xtruct>(); + insane.xtructs.add(truck); + System.out.print("testInsanity()"); + Map<Long,Map<Numberz,Insanity>> whoa = + testClient.testInsanity(insane); + System.out.print(" = {"); + for (long key : whoa.keySet()) { + Map<Numberz,Insanity> val = whoa.get(key); + System.out.print(key + " => {"); + + for (Numberz k2 : val.keySet()) { + Insanity v2 = val.get(k2); + System.out.print(k2 + " => {"); + Map<Numberz, Long> userMap = v2.userMap; + System.out.print("{"); + if (userMap != null) { + for (Numberz k3 : userMap.keySet()) { + System.out.print(k3 + " => " + userMap.get(k3) + ", "); + } + } + System.out.print("}, "); + + List<Xtruct> xtructs = v2.xtructs; + System.out.print("{"); + if (xtructs != null) { + for (Xtruct x : xtructs) { + System.out.print("{" + "\"" + x.string_thing + "\", " + x.byte_thing + ", " + x.i32_thing + ", "+ x.i64_thing + "}, "); + } + } + System.out.print("}"); + + System.out.print("}, "); + } + System.out.print("}, "); + } + System.out.print("}\n"); + } + + public boolean useAsyncProcessor() { + return false; + } + + public void testIt() throws Exception { + + for (TProtocolFactory protoFactory : getProtocols()) { + TProcessor processor = useAsyncProcessor() ? new ThriftTest.AsyncProcessor<AsyncTestHandler>(new AsyncTestHandler()) : new ThriftTest.Processor<TestHandler>(new TestHandler()); + + startServer(processor, protoFactory); + + TSocket socket = new TSocket(HOST, PORT); + socket.setTimeout(SOCKET_TIMEOUT); + TTransport transport = getClientTransport(socket); + + TProtocol protocol = protoFactory.getProtocol(transport); + ThriftTest.Client testClient = new ThriftTest.Client(protocol); + + open(transport); + testVoid(testClient); + testString(testClient); + testBool(testClient); + testByte(testClient); + testI32(testClient); + testI64(testClient); + testDouble(testClient); + testStruct(testClient); + testNestedStruct(testClient); + testMap(testClient); + testStringMap(testClient); + testSet(testClient); + testList(testClient); + testEnum(testClient); + testTypedef(testClient); + testNestedMap(testClient); + testInsanity(testClient); + testException(testClient); + testOneway(testClient); + testI32(testClient); + transport.close(); + socket.close(); + + stopServer(); + } + } + + public void open(TTransport transport) throws Exception { + transport.open(); + } + + public List<TProtocolFactory> getProtocols() { + return PROTOCOLS; + } + + private void testList(ThriftTest.Client testClient) throws TException { + List<Integer> listout = new ArrayList<Integer>(); + for (int i = -2; i < 3; ++i) { + listout.add(i); + } + List<Integer> listin = testClient.testList(listout); + assertEquals(listout, listin); + } + + private void testMap(ThriftTest.Client testClient) throws TException { + Map<Integer,Integer> mapout = new HashMap<Integer,Integer>(); + for (int i = 0; i < 5; ++i) { + mapout.put(i, i-10); + } + Map<Integer,Integer> mapin = testClient.testMap(mapout); + assertEquals(mapout, mapin); + } + + private void testStringMap(ThriftTest.Client testClient) throws TException { + Map<String,String> mapout = new HashMap<String,String>(); + mapout.put("a", "123"); + mapout.put(" x y ", " with spaces "); + mapout.put("same", "same"); + mapout.put("0", "numeric key"); + Map<String,String> mapin = testClient.testStringMap(mapout); + assertEquals(mapout, mapin); + } + + private void testNestedMap(ThriftTest.Client testClient) throws TException { + Map<Integer,Map<Integer,Integer>> mm = + testClient.testMapMap(1); + Map<Integer,Map<Integer,Integer>> mapmap = + new HashMap<Integer,Map<Integer,Integer>>(); + + HashMap<Integer,Integer> pos = new HashMap<Integer,Integer>(); + HashMap<Integer,Integer> neg = new HashMap<Integer,Integer>(); + for (int i = 1; i < 5; i++) { + pos.put(i, i); + neg.put(-i, -i); + } + + mapmap.put(4, pos); + mapmap.put(-4, neg); + assertEquals(mapmap, mm); + } + + private void testNestedStruct(ThriftTest.Client testClient) throws TException { + Xtruct2 in2 = testClient.testNest(XSTRUCT2); + assertEquals(XSTRUCT2, in2); + } + + private void testOneway(ThriftTest.Client testClient) throws Exception { + long begin = System.currentTimeMillis(); + testClient.testOneway(1); + long elapsed = System.currentTimeMillis() - begin; + assertTrue(elapsed < 500); + } + + private void testSet(ThriftTest.Client testClient) throws TException { + Set<Integer> setout = new HashSet<Integer>(); + for (int i = -2; i < 3; ++i) { + setout.add(i); + } + Set<Integer> setin = testClient.testSet(setout); + assertEquals(setout, setin); + } + + private void testString(ThriftTest.Client testClient) throws TException { + String s = testClient.testString("Test"); + assertEquals("Test", s); + } + + private void testStruct(ThriftTest.Client testClient) throws TException { + assertEquals(XSTRUCT, testClient.testStruct(XSTRUCT)); + } + + private void testTypedef(ThriftTest.Client testClient) throws TException { + assertEquals(309858235082523L, testClient.testTypedef(309858235082523L)); + } + + private void testVoid(ThriftTest.Client testClient) throws TException { + testClient.testVoid(); + } + + private static class CallCountingTransportFactory extends TTransportFactory { + public int count = 0; + private final Factory factory; + + public CallCountingTransportFactory(Factory factory) { + this.factory = factory; + } + + @Override + public TTransport getTransport(TTransport trans) { + count++; + return factory.getTransport(trans); + } + } + + public void testTransportFactory() throws Exception { + for (TProtocolFactory protoFactory : getProtocols()) { + TestHandler handler = new TestHandler(); + ThriftTest.Processor<TestHandler> processor = new ThriftTest.Processor<TestHandler>(handler); + + final CallCountingTransportFactory factory = new CallCountingTransportFactory(new TFramedTransport.Factory()); + + startServer(processor, protoFactory, factory); + assertEquals(0, factory.count); + + TSocket socket = new TSocket(HOST, PORT); + socket.setTimeout(SOCKET_TIMEOUT); + TTransport transport = getClientTransport(socket); + open(transport); + + TProtocol protocol = protoFactory.getProtocol(transport); + ThriftTest.Client testClient = new ThriftTest.Client(protocol); + assertEquals(0, testClient.testByte((byte) 0)); + assertEquals(2, factory.count); + socket.close(); + stopServer(); + } + } + + private void testException(ThriftTest.Client testClient) throws TException, Xception { + try { + testClient.testException("Xception"); + assert false; + } catch(Xception e) { + assertEquals(e.message, "Xception"); + assertEquals(e.errorCode, 1001); + } + try { + testClient.testException("TException"); + assert false; + } catch(TException e) { + } + testClient.testException("no Exception"); + } + + + public static class AsyncTestHandler implements ThriftTest.AsyncIface { + + TestHandler handler = new TestHandler(); + + @Override + public void testVoid(AsyncMethodCallback<Void> resultHandler) throws TException { + resultHandler.onComplete(null); + } + + @Override + public void testString(String thing, AsyncMethodCallback<String> resultHandler) throws TException { + resultHandler.onComplete(handler.testString(thing)); + } + + @Override + public void testBool(boolean thing, AsyncMethodCallback<Boolean> resultHandler) throws TException { + resultHandler.onComplete(handler.testBool(thing)); + } + + @Override + public void testByte(byte thing, AsyncMethodCallback<Byte> resultHandler) throws TException { + resultHandler.onComplete(handler.testByte(thing)); + } + + @Override + public void testI32(int thing, AsyncMethodCallback<Integer> resultHandler) throws TException { + resultHandler.onComplete(handler.testI32(thing)); + } + + @Override + public void testI64(long thing, AsyncMethodCallback<Long> resultHandler) throws TException { + resultHandler.onComplete(handler.testI64(thing)); + } + + @Override + public void testDouble(double thing, AsyncMethodCallback<Double> resultHandler) throws TException { + resultHandler.onComplete(handler.testDouble(thing)); + } + + @Override + public void testBinary(ByteBuffer thing, AsyncMethodCallback<ByteBuffer> resultHandler) throws TException { + resultHandler.onComplete(handler.testBinary(thing)); + } + + @Override + public void testStruct(Xtruct thing, AsyncMethodCallback<Xtruct> resultHandler) throws TException { + resultHandler.onComplete(handler.testStruct(thing)); + } + + @Override + public void testNest(Xtruct2 thing, AsyncMethodCallback<Xtruct2> resultHandler) throws TException { + resultHandler.onComplete(handler.testNest(thing)); + } + + @Override + public void testMap(Map<Integer, Integer> thing, AsyncMethodCallback<Map<Integer, Integer>> resultHandler) throws TException { + resultHandler.onComplete(handler.testMap(thing)); + } + + @Override + public void testStringMap(Map<String, String> thing, AsyncMethodCallback<Map<String, String>> resultHandler) throws TException { + resultHandler.onComplete(handler.testStringMap(thing)); + } + + @Override + public void testSet(Set<Integer> thing, AsyncMethodCallback<Set<Integer>> resultHandler) throws TException { + resultHandler.onComplete(handler.testSet(thing)); + } + + @Override + public void testList(List<Integer> thing, AsyncMethodCallback<List<Integer>> resultHandler) throws TException { + resultHandler.onComplete(handler.testList(thing)); + } + + @Override + public void testEnum(Numberz thing, AsyncMethodCallback<Numberz> resultHandler) throws TException { + resultHandler.onComplete(handler.testEnum(thing)); + } + + @Override + public void testTypedef(long thing, AsyncMethodCallback<Long> resultHandler) throws TException { + resultHandler.onComplete(handler.testTypedef(thing)); + } + + @Override + public void testMapMap(int hello, AsyncMethodCallback<Map<Integer,Map<Integer,Integer>>> resultHandler) throws TException { + resultHandler.onComplete(handler.testMapMap(hello)); + } + + @Override + public void testInsanity(Insanity argument, AsyncMethodCallback<Map<Long, Map<Numberz,Insanity>>> resultHandler) throws TException { + resultHandler.onComplete(handler.testInsanity(argument)); + } + + @Override + public void testMulti(byte arg0, int arg1, long arg2, Map<Short, String> arg3, Numberz arg4, long arg5, AsyncMethodCallback<Xtruct> resultHandler) throws TException { + resultHandler.onComplete(handler.testMulti(arg0,arg1,arg2,arg3,arg4,arg5)); + } + + @Override + public void testException(String arg, AsyncMethodCallback<Void> resultHandler) throws TException { + System.out.print("testException("+arg+")\n"); + if ("Xception".equals(arg)) { + Xception x = new Xception(); + x.errorCode = 1001; + x.message = arg; + // throw and onError yield the same result. + // throw x; + resultHandler.onError(x); + return; + } else if ("TException".equals(arg)) { + // throw and onError yield the same result. + // resultHandler.onError(new TException(arg)); + // return; + // Unspecified exception should yield a TApplicationException on client side + throw new RuntimeException(arg); + } + resultHandler.onComplete(null); + } + + @Override + public void testMultiException(String arg0, String arg1, AsyncMethodCallback<Xtruct> resultHandler) throws TException { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void testOneway(int secondsToSleep, AsyncMethodCallback<Void> resultHandler) throws TException { + handler.testOneway(secondsToSleep); + resultHandler.onComplete(null); + } + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java new file mode 100644 index 000000000..29c54cbbc --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestAsyncServer.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +public class TestAsyncServer extends TestNonblockingServer { + + @Override + public boolean useAsyncProcessor(){ + return true; + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java new file mode 100644 index 000000000..6638a333f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestHsHaServer.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.THsHaServer.Args; +import org.apache.thrift.transport.TNonblockingServerSocket; + +public class TestHsHaServer extends TestNonblockingServer { + protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory) { + return new THsHaServer(new Args(socket).processor(processor).protocolFactory(protoFactory)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java new file mode 100644 index 000000000..3df3bd827 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.TNonblockingServer.Args; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.TTransportFactory; + +import thrift.test.ThriftTest; + +public class TestNonblockingServer extends ServerTestBase { + + private Thread serverThread; + private TServer server; + private static final int NUM_QUERIES = 1000; + + protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory, TTransportFactory factory) { + final Args args = new Args(socket).processor(processor).protocolFactory(protoFactory); + if (factory != null) { + args.transportFactory(factory); + } + return new TNonblockingServer(args); + } + + @Override + public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception { + serverThread = new Thread() { + public void run() { + try { + // Transport + TNonblockingServerSocket tServerSocket = + new TNonblockingServerSocket(new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(PORT)); + + server = getServer(processor, tServerSocket, protoFactory, factory); + + // Run it + System.out.println("Starting the server on port " + PORT + "..."); + server.serve(); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + }; + serverThread.start(); + Thread.sleep(1000); + } + + @Override + public void stopServer() throws Exception { + server.stop(); + try { + serverThread.join(); + } catch (InterruptedException e) {} + } + + @Override + public TTransport getClientTransport(TTransport underlyingTransport) throws Exception { + return new TFramedTransport(underlyingTransport); + } + + + public void testCleanupAllSelectionKeys() throws Exception { + for (TProtocolFactory protoFactory : getProtocols()) { + TestHandler handler = new TestHandler(); + ThriftTest.Processor processor = new ThriftTest.Processor(handler); + + startServer(processor, protoFactory); + + TSocket socket = new TSocket(HOST, PORT); + socket.setTimeout(SOCKET_TIMEOUT); + TTransport transport = getClientTransport(socket); + + TProtocol protocol = protoFactory.getProtocol(transport); + ThriftTest.Client testClient = new ThriftTest.Client(protocol); + + open(transport); + + for (int i = 0; i < NUM_QUERIES; ++i) { + testClient.testI32(1); + } + server.stop(); + for (int i = 0; i < NUM_QUERIES; ++i) { + try { + testClient.testI32(1); + } catch(TTransportException e) { + System.err.println(e); + e.printStackTrace(); + if (e.getCause() instanceof java.net.SocketTimeoutException) { + fail("timed out when it should have thrown another kind of error!"); + } + } + } + + transport.close(); + stopServer(); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java new file mode 100644 index 000000000..ed729a296 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/server/TestThreadedSelectorServer.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.server; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.TThreadedSelectorServer.Args; +import org.apache.thrift.transport.TNonblockingServerSocket; + +public class TestThreadedSelectorServer extends TestNonblockingServer { + protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory) { + return new TThreadedSelectorServer(new Args(socket).processor(processor).protocolFactory(protoFactory)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java new file mode 100644 index 000000000..94ba543a8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/EqualityTest.java @@ -0,0 +1,663 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* +This program was generated by the following Python script: + +#!/usr/bin/python2.5 + +# Remove this when Python 2.6 hits the streets. +from __future__ import with_statement + +import sys +import os.path + + +# Quines the easy way. +with open(sys.argv[0], 'r') as handle: + source = handle.read() + +with open(os.path.join(os.path.dirname(sys.argv[0]), 'EqualityTest.java'), 'w') as out: + print >> out, ("/""*" r""" + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + """ "*""/") + print >> out + print >> out, "/""*" + print >> out, "This program was generated by the following Python script:" + print >> out + out.write(source) + print >> out, "*""/" + + print >> out, r''' +package org.apache.thrift.test; + +// Generated code +import thrift.test.*; + +/'''r'''** + *'''r'''/ +public class EqualityTest { + public static void main(String[] args) throws Exception { + JavaTestHelper lhs, rhs; +''' + + vals = { + 'int': ("1", "2"), + 'obj': ("\"foo\"", "\"bar\""), + 'bin': ("new byte[]{1,2}", "new byte[]{3,4}"), + } + matrix = ( + (False,False), + (False,True ), + (True ,False), + (True ,True ), + ) + + for type in ('int', 'obj', 'bin'): + for option in ('req', 'opt'): + nulls = matrix[0:1] if type == 'int' else matrix[-1::-1] + issets = matrix + for is_null in nulls: + for is_set in issets: + # isset is implied for non-primitives, so only consider the case + # where isset and non-null match. + if type != 'int' and list(is_set) != [ not null for null in is_null ]: + continue + for equal in (True, False): + print >> out + print >> out, " lhs = new JavaTestHelper();" + print >> out, " rhs = new JavaTestHelper();" + print >> out, " lhs." + option + "_" + type, "=", vals[type][0] + ";" + print >> out, " rhs." + option + "_" + type, "=", vals[type][0 if equal else 1] + ";" + isset_setter = "set" + option[0].upper() + option[1:] + "_" + type + "IsSet" + if (type == 'int' and is_set[0]): print >> out, " lhs." + isset_setter + "(true);" + if (type == 'int' and is_set[1]): print >> out, " rhs." + isset_setter + "(true);" + if (is_null[0]): print >> out, " lhs." + option + "_" + type, "= null;" + if (is_null[1]): print >> out, " rhs." + option + "_" + type, "= null;" + this_present = not is_null[0] and (option == 'req' or is_set[0]) + that_present = not is_null[1] and (option == 'req' or is_set[1]) + print >> out, " // this_present = " + repr(this_present) + print >> out, " // that_present = " + repr(that_present) + is_equal = \ + (not this_present and not that_present) or \ + (this_present and that_present and equal) + eq_str = 'true' if is_equal else 'false' + + print >> out, " if (lhs.equals(rhs) != "+eq_str+")" + print >> out, " throw new RuntimeException(\"Failure\");" + if is_equal: + print >> out, " if (lhs.hashCode() != rhs.hashCode())" + print >> out, " throw new RuntimeException(\"Failure\");" + + print >> out, r''' + } +} +''' +*/ + +package org.apache.thrift.test; + +// Generated code +import java.nio.ByteBuffer; + +import thrift.test.JavaTestHelper; + +/** + */ +public class EqualityTest { + public static void main(String[] args) throws Exception { + JavaTestHelper lhs, rhs; + + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 1; + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 2; + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 1; + rhs.setReq_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 2; + rhs.setReq_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 1; + lhs.setReq_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 2; + lhs.setReq_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 1; + lhs.setReq_intIsSet(true); + rhs.setReq_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_int = 1; + rhs.req_int = 2; + lhs.setReq_intIsSet(true); + rhs.setReq_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 1; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 2; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 1; + rhs.setOpt_intIsSet(true); + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 2; + rhs.setOpt_intIsSet(true); + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 1; + lhs.setOpt_intIsSet(true); + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 2; + lhs.setOpt_intIsSet(true); + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 1; + lhs.setOpt_intIsSet(true); + rhs.setOpt_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_int = 1; + rhs.opt_int = 2; + lhs.setOpt_intIsSet(true); + rhs.setOpt_intIsSet(true); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "foo"; + lhs.req_obj = null; + rhs.req_obj = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "bar"; + lhs.req_obj = null; + rhs.req_obj = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "foo"; + lhs.req_obj = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "bar"; + lhs.req_obj = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "foo"; + rhs.req_obj = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "bar"; + rhs.req_obj = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "foo"; + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_obj = "foo"; + rhs.req_obj = "bar"; + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "foo"; + lhs.opt_obj = null; + rhs.opt_obj = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "bar"; + lhs.opt_obj = null; + rhs.opt_obj = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "foo"; + lhs.opt_obj = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "bar"; + lhs.opt_obj = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "foo"; + rhs.opt_obj = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "bar"; + rhs.opt_obj = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "foo"; + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_obj = "foo"; + rhs.opt_obj = "bar"; + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + lhs.req_bin = null; + rhs.req_bin = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4}); + lhs.req_bin = null; + rhs.req_bin = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + lhs.req_bin = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4}); + lhs.req_bin = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4}); + rhs.req_bin = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.req_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.req_bin = ByteBuffer.wrap(new byte[]{3,4}); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + lhs.opt_bin = null; + rhs.opt_bin = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4}); + lhs.opt_bin = null; + rhs.opt_bin = null; + // this_present = False + // that_present = False + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + lhs.opt_bin = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4}); + lhs.opt_bin = null; + // this_present = False + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4}); + rhs.opt_bin = null; + // this_present = True + // that_present = False + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != true) + throw new RuntimeException("Failure"); + if (lhs.hashCode() != rhs.hashCode()) + throw new RuntimeException("Failure"); + + lhs = new JavaTestHelper(); + rhs = new JavaTestHelper(); + lhs.opt_bin = ByteBuffer.wrap(new byte[]{1,2}); + rhs.opt_bin = ByteBuffer.wrap(new byte[]{3,4}); + // this_present = True + // that_present = True + if (lhs.equals(rhs) != false) + throw new RuntimeException("Failure"); + + } +} + diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java new file mode 100644 index 000000000..6a2a0ed0c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/JavaBeansTest.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import java.nio.ByteBuffer; +import java.util.LinkedList; + +import thrift.test.OneOfEachBeans; + +public class JavaBeansTest { + public static void main(String[] args) throws Exception { + // Test isSet methods + OneOfEachBeans ooe = new OneOfEachBeans(); + + // Nothing should be set + if (ooe.is_set_a_bite()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_base64()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_byte_list()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_double_precision()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_i16_list()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_i64_list()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_boolean_field()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_integer16()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_integer32()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_integer64()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + if (ooe.is_set_some_characters()) + throw new RuntimeException("isSet method error: unset field returned as set!"); + + for (int i = 1; i < 12; i++){ + if (ooe.isSet(ooe.fieldForId(i))) + throw new RuntimeException("isSet method error: unset field " + i + " returned as set!"); + } + + // Everything is set + ooe.set_a_bite((byte) 1); + ooe.set_base64(ByteBuffer.wrap("bytes".getBytes())); + ooe.set_byte_list(new LinkedList<Byte>()); + ooe.set_double_precision(1); + ooe.set_i16_list(new LinkedList<Short>()); + ooe.set_i64_list(new LinkedList<Long>()); + ooe.set_boolean_field(true); + ooe.set_integer16((short) 1); + ooe.set_integer32(1); + ooe.set_integer64(1); + ooe.set_some_characters("string"); + + if (!ooe.is_set_a_bite()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_base64()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_byte_list()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_double_precision()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_i16_list()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_i64_list()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_boolean_field()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_integer16()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_integer32()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_integer64()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + if (!ooe.is_set_some_characters()) + throw new RuntimeException("isSet method error: set field returned as unset!"); + + for (int i = 1; i < 12; i++){ + if (!ooe.isSet(ooe.fieldForId(i))) + throw new RuntimeException("isSet method error: set field " + i + " returned as unset!"); + } + + // Should throw exception when field doesn't exist + boolean exceptionThrown = false; + try{ + if (ooe.isSet(ooe.fieldForId(100))); + } catch (IllegalArgumentException e){ + exceptionThrown = true; + } + if (!exceptionThrown) + throw new RuntimeException("isSet method error: non-existent field provided as agument but no exception thrown!"); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java new file mode 100644 index 000000000..7e3b091d4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/ReadStruct.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; +import org.apache.thrift.transport.TTransport; + +import thrift.test.CompactProtoTestStruct; + +public class ReadStruct { + public static void main(String[] args) throws Exception { + if (args.length != 2) { + System.out.println("usage: java -cp build/classes org.apache.thrift.test.ReadStruct filename proto_factory_class"); + System.out.println("Read in an instance of CompactProtocolTestStruct from 'file', making sure that it is equivalent to Fixtures.compactProtoTestStruct. Use a protocol from 'proto_factory_class'."); + } + + TTransport trans = new TIOStreamTransport(new BufferedInputStream(new FileInputStream(args[0]))); + + TProtocolFactory factory = (TProtocolFactory)Class.forName(args[1]).newInstance(); + + TProtocol proto = factory.getProtocol(trans); + + CompactProtoTestStruct cpts = new CompactProtoTestStruct(); + + for (CompactProtoTestStruct._Fields fid : CompactProtoTestStruct.metaDataMap.keySet()) { + cpts.setFieldValue(fid, null); + } + + cpts.read(proto); + + if (cpts.equals(Fixtures.compactProtoTestStruct)) { + System.out.println("Object verified successfully!"); + } else { + System.out.println("Object failed verification!"); + System.out.println("Expected: " + Fixtures.compactProtoTestStruct + " but got " + cpts); + } + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java new file mode 100644 index 000000000..2b0db3132 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/SerializationBenchmark.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.TBase; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TMemoryBuffer; +import org.apache.thrift.transport.TMemoryInputTransport; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; + +import thrift.test.OneOfEach; + +public class SerializationBenchmark { + private final static int HOW_MANY = 10000000; + + public static void main(String[] args) throws Exception { + TProtocolFactory factory = new TBinaryProtocol.Factory(); + + testSerialization(factory, Fixtures.oneOfEach); + testDeserialization(factory, Fixtures.oneOfEach, OneOfEach.class); + } + + public static void testSerialization(TProtocolFactory factory, TBase object) throws Exception { + TTransport trans = new TTransport() { + public void write(byte[] bin, int x, int y) throws TTransportException {} + public int read(byte[] bin, int x, int y) throws TTransportException {return 0;} + public void close() {} + public void open() {} + public boolean isOpen() {return true;} + }; + + TProtocol proto = factory.getProtocol(trans); + + long startTime = System.currentTimeMillis(); + for (int i = 0; i < HOW_MANY; i++) { + object.write(proto); + } + long endTime = System.currentTimeMillis(); + + System.out.println("Serialization test time: " + (endTime - startTime) + " ms"); + } + + public static <T extends TBase> void testDeserialization(TProtocolFactory factory, T object, Class<T> klass) throws Exception { + TMemoryBuffer buf = new TMemoryBuffer(0); + object.write(factory.getProtocol(buf)); + byte[] serialized = new byte[100*1024]; + buf.read(serialized, 0, 100*1024); + + long startTime = System.currentTimeMillis(); + for (int i = 0; i < HOW_MANY; i++) { + T o2 = klass.newInstance(); + o2.read(factory.getProtocol(new TMemoryInputTransport(serialized))); + } + long endTime = System.currentTimeMillis(); + + System.out.println("Deserialization test time: " + (endTime - startTime) + " ms"); + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java new file mode 100644 index 000000000..84410cea0 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestClient.java @@ -0,0 +1,811 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.thrift.TApplicationException; +import org.apache.thrift.TException; +import org.apache.thrift.TSerializer; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TSimpleJSONProtocol; +import org.apache.thrift.transport.TFastFramedTransport; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.THttpClient; +import org.apache.thrift.transport.TSSLTransportFactory; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; + +// Generated code +import thrift.test.Insanity; +import thrift.test.Numberz; +import thrift.test.SecondService; +import thrift.test.ThriftTest; +import thrift.test.Xception; +import thrift.test.Xception2; +import thrift.test.Xtruct; +import thrift.test.Xtruct2; + +/** + * Test Java client for thrift. Essentially just a copy of the C++ version, + * this makes a variety of requests to enable testing for both performance and + * correctness of the output. + * + */ +public class TestClient { + + private static int ERR_BASETYPES = 1; + private static int ERR_STRUCTS = 2; + private static int ERR_CONTAINERS = 4; + private static int ERR_EXCEPTIONS = 8; + private static int ERR_PROTOCOLS = 16; + private static int ERR_UNKNOWN = 64; + + public static void main(String [] args) { + String host = "localhost"; + int port = 9090; + int numTests = 1; + String protocol_type = "binary"; + String transport_type = "buffered"; + boolean ssl = false; + + int socketTimeout = 1000; + + try { + for (int i = 0; i < args.length; ++i) { + if (args[i].startsWith("--host")) { + host = args[i].split("=")[1]; + host.trim(); + } else if (args[i].startsWith("--port")) { + port = Integer.valueOf(args[i].split("=")[1]); + } else if (args[i].startsWith("--n") || + args[i].startsWith("--testloops")){ + numTests = Integer.valueOf(args[i].split("=")[1]); + } else if (args[i].equals("--timeout")) { + socketTimeout = Integer.valueOf(args[i].split("=")[1]); + } else if (args[i].startsWith("--protocol")) { + protocol_type = args[i].split("=")[1]; + protocol_type.trim(); + } else if (args[i].startsWith("--transport")) { + transport_type = args[i].split("=")[1]; + transport_type.trim(); + } else if (args[i].equals("--ssl")) { + ssl = true; + } else if (args[i].equals("--help")) { + System.out.println("Allowed options:"); + System.out.println(" --help\t\t\tProduce help message"); + System.out.println(" --host=arg (=" + host + ")\tHost to connect"); + System.out.println(" --port=arg (=" + port + ")\tPort number to connect"); + System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed, http"); + System.out.println(" --protocol=arg (=" + protocol_type + ")\tProtocol: binary, compact, json, multi, multic, multij"); + System.out.println(" --ssl\t\t\tEncrypted Transport using SSL"); + System.out.println(" --testloops[--n]=arg (=" + numTests + ")\tNumber of Tests"); + System.exit(0); + } + } + } catch (Exception x) { + System.err.println("Can not parse arguments! See --help"); + System.exit(ERR_UNKNOWN); + } + + try { + if (protocol_type.equals("binary")) { + } else if (protocol_type.equals("compact")) { + } else if (protocol_type.equals("json")) { + } else if (protocol_type.equals("multi")) { + } else if (protocol_type.equals("multic")) { + } else if (protocol_type.equals("multij")) { + } else { + throw new Exception("Unknown protocol type! " + protocol_type); + } + if (transport_type.equals("buffered")) { + } else if (transport_type.equals("framed")) { + } else if (transport_type.equals("fastframed")) { + } else if (transport_type.equals("http")) { + } else { + throw new Exception("Unknown transport type! " + transport_type); + } + if (transport_type.equals("http") && ssl == true) { + throw new Exception("SSL is not supported over http."); + } + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + System.exit(ERR_UNKNOWN); + } + + TTransport transport = null; + + try { + if (transport_type.equals("http")) { + String url = "http://" + host + ":" + port + "/service"; + transport = new THttpClient(url); + } else { + TSocket socket = null; + if (ssl == true) { + socket = TSSLTransportFactory.getClientSocket(host, port, 0); + } else { + socket = new TSocket(host, port); + } + socket.setTimeout(socketTimeout); + transport = socket; + if (transport_type.equals("buffered")) { + } else if (transport_type.equals("framed")) { + transport = new TFramedTransport(transport); + } else if (transport_type.equals("fastframed")) { + transport = new TFastFramedTransport(transport); + } + } + } catch (Exception x) { + x.printStackTrace(); + System.exit(ERR_UNKNOWN); + } + + TProtocol tProtocol = null; + TProtocol tProtocol2 = null; + if (protocol_type.equals("json") || protocol_type.equals("multij")) { + tProtocol = new TJSONProtocol(transport); + } else if (protocol_type.equals("compact") || protocol_type.equals("multic")) { + tProtocol = new TCompactProtocol(transport); + } else { + tProtocol = new TBinaryProtocol(transport); + } + + if (protocol_type.startsWith("multi")) { + tProtocol2 = new TMultiplexedProtocol(tProtocol, "SecondService"); + tProtocol = new TMultiplexedProtocol(tProtocol, "ThriftTest"); + } + + ThriftTest.Client testClient = new ThriftTest.Client(tProtocol); + Insanity insane = new Insanity(); + + long timeMin = 0; + long timeMax = 0; + long timeTot = 0; + + int returnCode = 0; + for (int test = 0; test < numTests; ++test) { + try { + /** + * CONNECT TEST + */ + System.out.println("Test #" + (test+1) + ", " + "connect " + host + ":" + port); + + if (transport.isOpen() == false) { + try { + transport.open(); + } catch (TTransportException ttx) { + ttx.printStackTrace(); + System.out.println("Connect failed: " + ttx.getMessage()); + System.exit(ERR_UNKNOWN); + } + } + + long start = System.nanoTime(); + + /** + * VOID TEST + */ + try { + System.out.print("testVoid()"); + testClient.testVoid(); + System.out.print(" = void\n"); + } catch (TApplicationException tax) { + tax.printStackTrace(); + returnCode |= ERR_BASETYPES; + } + + /** + * STRING TEST + */ + System.out.print("testString(\"Test\")"); + String s = testClient.testString("Test"); + System.out.print(" = \"" + s + "\"\n"); + if (!s.equals("Test")) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + + /** + * Multiplexed test + */ + if (protocol_type.startsWith("multi")) { + SecondService.Client secondClient = new SecondService.Client(tProtocol2); + System.out.print("secondtestString(\"Test2\")"); + s = secondClient.secondtestString("Test2"); + System.out.print(" = \"" + s + "\"\n"); + if (!s.equals("testString(\"Test2\")")) { + returnCode |= ERR_PROTOCOLS; + System.out.println("*** FAILURE ***\n"); + } + } + /** + * BYTE TEST + */ + System.out.print("testByte(1)"); + byte i8 = testClient.testByte((byte)1); + System.out.print(" = " + i8 + "\n"); + if (i8 != 1) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + + /** + * I32 TEST + */ + System.out.print("testI32(-1)"); + int i32 = testClient.testI32(-1); + System.out.print(" = " + i32 + "\n"); + if (i32 != -1) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + + /** + * I64 TEST + */ + System.out.print("testI64(-34359738368)"); + long i64 = testClient.testI64(-34359738368L); + System.out.print(" = " + i64 + "\n"); + if (i64 != -34359738368L) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + + /** + * DOUBLE TEST + */ + System.out.print("testDouble(-5.325098235)"); + double dub = testClient.testDouble(-5.325098235); + System.out.print(" = " + dub + "\n"); + if (Math.abs(dub - (-5.325098235)) > 0.001) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + + /** + * BINARY TEST + */ + try { + System.out.print("testBinary(-128...127) = "); + byte[] data = new byte[] {-128, -127, -126, -125, -124, -123, -122, -121, -120, -119, -118, -117, -116, -115, -114, -113, -112, -111, -110, -109, -108, -107, -106, -105, -104, -103, -102, -101, -100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127}; + ByteBuffer bin = testClient.testBinary(ByteBuffer.wrap(data)); + bin.mark(); + byte[] bytes = new byte[bin.limit() - bin.position()]; + bin.get(bytes); + bin.reset(); + System.out.print("{"); + boolean first = true; + for (int i = 0; i < bytes.length; ++i) { + if (first) + first = false; + else + System.out.print(", "); + System.out.print(bytes[i]); + } + System.out.println("}"); + if (!ByteBuffer.wrap(data).equals(bin)) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + } catch (Exception ex) { + returnCode |= ERR_BASETYPES; + System.out.println("\n*** FAILURE ***\n"); + ex.printStackTrace(System.out); + } + + /** + * STRUCT TEST + */ + System.out.print("testStruct({\"Zero\", 1, -3, -5})"); + Xtruct out = new Xtruct(); + out.string_thing = "Zero"; + out.byte_thing = (byte) 1; + out.i32_thing = -3; + out.i64_thing = -5; + Xtruct in = testClient.testStruct(out); + System.out.print(" = {" + "\"" + + in.string_thing + "\"," + + in.byte_thing + ", " + + in.i32_thing + ", " + + in.i64_thing + "}\n"); + if (!in.equals(out)) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * NESTED STRUCT TEST + */ + System.out.print("testNest({1, {\"Zero\", 1, -3, -5}), 5}"); + Xtruct2 out2 = new Xtruct2(); + out2.byte_thing = (short)1; + out2.struct_thing = out; + out2.i32_thing = 5; + Xtruct2 in2 = testClient.testNest(out2); + in = in2.struct_thing; + System.out.print(" = {" + in2.byte_thing + ", {" + "\"" + + in.string_thing + "\", " + + in.byte_thing + ", " + + in.i32_thing + ", " + + in.i64_thing + "}, " + + in2.i32_thing + "}\n"); + if (!in2.equals(out2)) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * MAP TEST + */ + Map<Integer,Integer> mapout = new HashMap<Integer,Integer>(); + for (int i = 0; i < 5; ++i) { + mapout.put(i, i-10); + } + System.out.print("testMap({"); + boolean first = true; + for (int key : mapout.keySet()) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(key + " => " + mapout.get(key)); + } + System.out.print("})"); + Map<Integer,Integer> mapin = testClient.testMap(mapout); + System.out.print(" = {"); + first = true; + for (int key : mapin.keySet()) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(key + " => " + mapout.get(key)); + } + System.out.print("}\n"); + if (!mapout.equals(mapin)) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * STRING MAP TEST + */ + try { + Map<String, String> smapout = new HashMap<String, String>(); + smapout.put("a", "2"); + smapout.put("b", "blah"); + smapout.put("some", "thing"); + for (String key : smapout.keySet()) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(key + " => " + smapout.get(key)); + } + System.out.print("})"); + Map<String, String> smapin = testClient.testStringMap(smapout); + System.out.print(" = {"); + first = true; + for (String key : smapin.keySet()) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(key + " => " + smapout.get(key)); + } + System.out.print("}\n"); + if (!smapout.equals(smapin)) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + } + } catch (Exception ex) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + ex.printStackTrace(System.out); + } + + /** + * SET TEST + */ + Set<Integer> setout = new HashSet<Integer>(); + for (int i = -2; i < 3; ++i) { + setout.add(i); + } + System.out.print("testSet({"); + first = true; + for (int elem : setout) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(elem); + } + System.out.print("})"); + Set<Integer> setin = testClient.testSet(setout); + System.out.print(" = {"); + first = true; + for (int elem : setin) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(elem); + } + System.out.print("}\n"); + if (!setout.equals(setin)) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * LIST TEST + */ + List<Integer> listout = new ArrayList<Integer>(); + for (int i = -2; i < 3; ++i) { + listout.add(i); + } + System.out.print("testList({"); + first = true; + for (int elem : listout) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(elem); + } + System.out.print("})"); + List<Integer> listin = testClient.testList(listout); + System.out.print(" = {"); + first = true; + for (int elem : listin) { + if (first) { + first = false; + } else { + System.out.print(", "); + } + System.out.print(elem); + } + System.out.print("}\n"); + if (!listout.equals(listin)) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * ENUM TEST + */ + System.out.print("testEnum(ONE)"); + Numberz ret = testClient.testEnum(Numberz.ONE); + System.out.print(" = " + ret + "\n"); + if (ret != Numberz.ONE) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + System.out.print("testEnum(TWO)"); + ret = testClient.testEnum(Numberz.TWO); + System.out.print(" = " + ret + "\n"); + if (ret != Numberz.TWO) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + System.out.print("testEnum(THREE)"); + ret = testClient.testEnum(Numberz.THREE); + System.out.print(" = " + ret + "\n"); + if (ret != Numberz.THREE) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + System.out.print("testEnum(FIVE)"); + ret = testClient.testEnum(Numberz.FIVE); + System.out.print(" = " + ret + "\n"); + if (ret != Numberz.FIVE) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + System.out.print("testEnum(EIGHT)"); + ret = testClient.testEnum(Numberz.EIGHT); + System.out.print(" = " + ret + "\n"); + if (ret != Numberz.EIGHT) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * TYPEDEF TEST + */ + System.out.print("testTypedef(309858235082523)"); + long uid = testClient.testTypedef(309858235082523L); + System.out.print(" = " + uid + "\n"); + if (uid != 309858235082523L) { + returnCode |= ERR_BASETYPES; + System.out.println("*** FAILURE ***\n"); + } + + /** + * NESTED MAP TEST + */ + System.out.print("testMapMap(1)"); + Map<Integer,Map<Integer,Integer>> mm = + testClient.testMapMap(1); + System.out.print(" = {"); + for (int key : mm.keySet()) { + System.out.print(key + " => {"); + Map<Integer,Integer> m2 = mm.get(key); + for (int k2 : m2.keySet()) { + System.out.print(k2 + " => " + m2.get(k2) + ", "); + } + System.out.print("}, "); + } + System.out.print("}\n"); + if (mm.size() != 2 || !mm.containsKey(4) || !mm.containsKey(-4)) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + } else { + Map<Integer, Integer> m1 = mm.get(4); + Map<Integer, Integer> m2 = mm.get(-4); + if (m1.get(1) != 1 || m1.get(2) != 2 || m1.get(3) != 3 || m1.get(4) != 4 || + m2.get(-1) != -1 || m2.get(-2) != -2 || m2.get(-3) != -3 || m2.get(-4) != -4) { + returnCode |= ERR_CONTAINERS; + System.out.println("*** FAILURE ***\n"); + } + } + + /** + * INSANITY TEST + */ + + boolean insanityFailed = true; + try { + Xtruct hello = new Xtruct(); + hello.string_thing = "Hello2"; + hello.byte_thing = 2; + hello.i32_thing = 2; + hello.i64_thing = 2; + + Xtruct goodbye = new Xtruct(); + goodbye.string_thing = "Goodbye4"; + goodbye.byte_thing = (byte)4; + goodbye.i32_thing = 4; + goodbye.i64_thing = (long)4; + + insane.userMap = new HashMap<Numberz, Long>(); + insane.userMap.put(Numberz.EIGHT, (long)8); + insane.userMap.put(Numberz.FIVE, (long)5); + insane.xtructs = new ArrayList<Xtruct>(); + insane.xtructs.add(goodbye); + insane.xtructs.add(hello); + + System.out.print("testInsanity()"); + Map<Long,Map<Numberz,Insanity>> whoa = + testClient.testInsanity(insane); + System.out.print(" = {"); + for (long key : whoa.keySet()) { + Map<Numberz,Insanity> val = whoa.get(key); + System.out.print(key + " => {"); + + for (Numberz k2 : val.keySet()) { + Insanity v2 = val.get(k2); + System.out.print(k2 + " => {"); + Map<Numberz, Long> userMap = v2.userMap; + System.out.print("{"); + if (userMap != null) { + for (Numberz k3 : userMap.keySet()) { + System.out.print(k3 + " => " + userMap.get(k3) + ", "); + } + } + System.out.print("}, "); + + List<Xtruct> xtructs = v2.xtructs; + System.out.print("{"); + if (xtructs != null) { + for (Xtruct x : xtructs) { + System.out.print("{" + "\"" + x.string_thing + "\", " + x.byte_thing + ", " + x.i32_thing + ", "+ x.i64_thing + "}, "); + } + } + System.out.print("}"); + + System.out.print("}, "); + } + System.out.print("}, "); + } + System.out.print("}\n"); + if (whoa.size() == 2 && whoa.containsKey(1L) && whoa.containsKey(2L)) { + Map<Numberz, Insanity> first_map = whoa.get(1L); + Map<Numberz, Insanity> second_map = whoa.get(2L); + if (first_map.size() == 2 && + first_map.containsKey(Numberz.TWO) && + first_map.containsKey(Numberz.THREE) && + second_map.size() == 1 && + second_map.containsKey(Numberz.SIX) && + insane.equals(first_map.get(Numberz.TWO)) && + insane.equals(first_map.get(Numberz.THREE))) { + Insanity six =second_map.get(Numberz.SIX); + // Cannot use "new Insanity().equals(six)" because as of now, struct/container + // fields with default requiredness have isset=false for local instances and yet + // received empty values from other languages like C++ have isset=true . + if (six.getUserMapSize() == 0 && six.getXtructsSize() == 0) { + // OK + insanityFailed = false; + } + } + } + } catch (Exception ex) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + ex.printStackTrace(System.out); + insanityFailed = false; + } + if (insanityFailed) { + returnCode |= ERR_STRUCTS; + System.out.println("*** FAILURE ***\n"); + } + + /** + * EXECPTION TEST + */ + try { + System.out.print("testClient.testException(\"Xception\") =>"); + testClient.testException("Xception"); + System.out.print(" void\n*** FAILURE ***\n"); + returnCode |= ERR_EXCEPTIONS; + } catch(Xception e) { + System.out.printf(" {%d, \"%s\"}\n", e.errorCode, e.message); + } + + try { + System.out.print("testClient.testException(\"TException\") =>"); + testClient.testException("TException"); + System.out.print(" void\n*** FAILURE ***\n"); + returnCode |= ERR_EXCEPTIONS; + } catch(TException e) { + System.out.printf(" {\"%s\"}\n", e.getMessage()); + } + + try { + System.out.print("testClient.testException(\"success\") =>"); + testClient.testException("success"); + System.out.print(" void\n"); + }catch(Exception e) { + System.out.printf(" exception\n*** FAILURE ***\n"); + returnCode |= ERR_EXCEPTIONS; + } + + + /** + * MULTI EXCEPTION TEST + */ + + try { + System.out.printf("testClient.testMultiException(\"Xception\", \"test 1\") =>"); + testClient.testMultiException("Xception", "test 1"); + System.out.print(" result\n*** FAILURE ***\n"); + returnCode |= ERR_EXCEPTIONS; + } catch(Xception e) { + System.out.printf(" {%d, \"%s\"}\n", e.errorCode, e.message); + } + + try { + System.out.printf("testClient.testMultiException(\"Xception2\", \"test 2\") =>"); + testClient.testMultiException("Xception2", "test 2"); + System.out.print(" result\n*** FAILURE ***\n"); + returnCode |= ERR_EXCEPTIONS; + } catch(Xception2 e) { + System.out.printf(" {%d, {\"%s\"}}\n", e.errorCode, e.struct_thing.string_thing); + } + + try { + System.out.print("testClient.testMultiException(\"success\", \"test 3\") =>"); + Xtruct result; + result = testClient.testMultiException("success", "test 3"); + System.out.printf(" {{\"%s\"}}\n", result.string_thing); + } catch(Exception e) { + System.out.printf(" exception\n*** FAILURE ***\n"); + returnCode |= ERR_EXCEPTIONS; + } + + + + /** + * ONEWAY TEST + */ + System.out.print("testOneway(3)..."); + long startOneway = System.nanoTime(); + testClient.testOneway(3); + long onewayElapsedMillis = (System.nanoTime() - startOneway) / 1000000; + if (onewayElapsedMillis > 200) { + System.out.println("Oneway test took too long to execute failed: took " + + Long.toString(onewayElapsedMillis) + + "ms"); + System.out.println("oneway calls are 'fire and forget' and therefore should not cause blocking."); + System.out.println("Some transports (HTTP) have a required response, and typically this failure"); + System.out.println("means the transport response was delayed until after the execution"); + System.out.println("of the RPC. The server should post the transport response immediately and"); + System.out.println("before executing the RPC."); + System.out.println("*** FAILURE ***"); + returnCode |= ERR_BASETYPES; + } else { + System.out.println("Success - fire and forget only took " + + Long.toString(onewayElapsedMillis) + + "ms"); + } + + + long stop = System.nanoTime(); + long tot = stop-start; + + System.out.println("Total time: " + tot/1000 + "us"); + + if (timeMin == 0 || tot < timeMin) { + timeMin = tot; + } + if (tot > timeMax) { + timeMax = tot; + } + timeTot += tot; + + transport.close(); + } catch (Exception x) { + System.out.printf("*** FAILURE ***\n"); + x.printStackTrace(); + returnCode |= ERR_UNKNOWN; + } + } + + long timeAvg = timeTot / numTests; + + System.out.println("Min time: " + timeMin/1000 + "us"); + System.out.println("Max time: " + timeMax/1000 + "us"); + System.out.println("Avg time: " + timeAvg/1000 + "us"); + + try { + String json = (new TSerializer(new TSimpleJSONProtocol.Factory())).toString(insane); + System.out.println("\nSample TSimpleJSONProtocol output:\n" + json); + } catch (TException x) { + System.out.println("*** FAILURE ***"); + x.printStackTrace(); + returnCode |= ERR_BASETYPES; + } + + + System.exit(returnCode); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java new file mode 100644 index 000000000..41c4b6500 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestNonblockingServer.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import org.apache.thrift.server.THsHaServer; +import org.apache.thrift.server.TNonblockingServer; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.THsHaServer.Args; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.server.ServerTestBase.TestHandler; + +import thrift.test.ThriftTest; + + +public class TestNonblockingServer extends TestServer { + public static void main(String [] args) { + try { + int port = 9090; + boolean hsha = false; + + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-p")) { + port = Integer.valueOf(args[i++]); + } else if (args[i].equals("-hsha")) { + hsha = true; + } + } + //@TODO add other protocol and transport types + + // Processor + TestHandler testHandler = + new TestHandler(); + ThriftTest.Processor testProcessor = + new ThriftTest.Processor(testHandler); + + // Transport + TNonblockingServerSocket tServerSocket = + new TNonblockingServerSocket(new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(port)); + + TServer serverEngine; + + if (hsha) { + // HsHa Server + serverEngine = new THsHaServer(new Args(tServerSocket).processor(testProcessor)); + } else { + // Nonblocking Server + serverEngine = new TNonblockingServer(new Args(tServerSocket).processor(testProcessor)); + } + + // Run it + System.out.println("Starting the server on port " + port + "..."); + serverEngine.serve(); + + } catch (Exception x) { + x.printStackTrace(); + } + System.out.println("done."); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java new file mode 100644 index 000000000..1f3e555d9 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/TestServer.java @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TJSONProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.server.ServerContext; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.TServer.Args; +import org.apache.thrift.server.TSimpleServer; +import org.apache.thrift.server.TThreadPoolServer; +import org.apache.thrift.server.ServerTestBase.TestHandler; +import org.apache.thrift.server.TServerEventHandler; +import org.apache.thrift.server.TThreadedSelectorServer; +import org.apache.thrift.server.TNonblockingServer; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TFastFramedTransport; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TSSLTransportFactory; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportFactory; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.TMultiplexedProcessor; + +import thrift.test.Insanity; +import thrift.test.Numberz; +import thrift.test.SecondService; +import thrift.test.ThriftTest; +import thrift.test.Xception; +import thrift.test.Xception2; +import thrift.test.Xtruct; +import thrift.test.Xtruct2; + +public class TestServer { + + // Multiplexed Protocol Support Details: + // + // For multiplexed testing we always use binary protocol underneath. + // + // "ThriftTest" named service implements "ThriftTest" from ThriftTest.thrift + // "SecondService" named service implements "SecondService" from ThriftTest.thrift + // In addition, to support older non-multiplexed clients using the same concrete protocol + // the multiplexed processor is taught to use "ThriftTest" if the incoming request has no + // multiplexed call name decoration. + + static class SecondHandler implements thrift.test.SecondService.Iface { + + @Override + public java.lang.String secondtestString(java.lang.String thing) throws org.apache.thrift.TException + { return "testString(\"" + thing + "\")"; } + + } + + static class TestServerContext implements ServerContext { + + int connectionId; + + public TestServerContext(int connectionId) { + this.connectionId = connectionId; + } + + public int getConnectionId() { + return connectionId; + } + + public void setConnectionId(int connectionId) { + this.connectionId = connectionId; + } + + } + + static class TestServerEventHandler implements TServerEventHandler { + + private int nextConnectionId = 1; + + public void preServe() { + System.out.println("TServerEventHandler.preServe - called only once before server starts accepting connections"); + } + + public ServerContext createContext(TProtocol input, TProtocol output) { + //we can create some connection level data which is stored while connection is alive & served + TestServerContext ctx = new TestServerContext(nextConnectionId++); + System.out.println("TServerEventHandler.createContext - connection #"+ctx.getConnectionId()+" established"); + return ctx; + } + + public void deleteContext(ServerContext serverContext, TProtocol input, TProtocol output) { + TestServerContext ctx = (TestServerContext)serverContext; + System.out.println("TServerEventHandler.deleteContext - connection #"+ctx.getConnectionId()+" terminated"); + } + + public void processContext(ServerContext serverContext, TTransport inputTransport, TTransport outputTransport) { + TestServerContext ctx = (TestServerContext)serverContext; + System.out.println("TServerEventHandler.processContext - connection #"+ctx.getConnectionId()+" is ready to process next request"); + } + + } + + public static void main(String [] args) { + try { + int port = 9090; + boolean ssl = false; + String transport_type = "buffered"; + String protocol_type = "binary"; + String server_type = "thread-pool"; + String domain_socket = ""; + int string_limit = -1; + int container_limit = -1; + try { + for (int i = 0; i < args.length; i++) { + if (args[i].startsWith("--port")) { + port = Integer.valueOf(args[i].split("=")[1]); + } else if (args[i].startsWith("--server-type")) { + server_type = args[i].split("=")[1]; + server_type.trim(); + } else if (args[i].startsWith("--port")) { + port=Integer.parseInt(args[i].split("=")[1]); + } else if (args[i].startsWith("--protocol")) { + protocol_type = args[i].split("=")[1]; + protocol_type.trim(); + } else if (args[i].startsWith("--transport")) { + transport_type = args[i].split("=")[1]; + transport_type.trim(); + } else if (args[i].equals("--ssl")) { + ssl = true; + } else if (args[i].startsWith("--string-limit")) { + string_limit = Integer.valueOf(args[i].split("=")[1]); + } else if (args[i].startsWith("--container-limit")) { + container_limit = Integer.valueOf(args[i].split("=")[1]); + } else if (args[i].equals("--help")) { + System.out.println("Allowed options:"); + System.out.println(" --help\t\t\tProduce help message"); + System.out.println(" --port=arg (=" + port + ")\tPort number to connect"); + System.out.println(" --transport=arg (=" + transport_type + ")\n\t\t\t\tTransport: buffered, framed, fastframed"); + System.out.println(" --protocol=arg (=" + protocol_type + ")\tProtocol: binary, compact, json, multi, multic, multij"); + System.out.println(" --ssl\t\t\tEncrypted Transport using SSL"); + System.out.println(" --server-type=arg (=" + server_type +")\n\t\t\t\tType of server: simple, thread-pool, nonblocking, threaded-selector"); + System.out.println(" --string-limit=arg (=" + string_limit + ")\tString read length limit"); + System.out.println(" --container-limit=arg (=" + container_limit + ")\tContainer read length limit"); + System.exit(0); + } + } + } catch (Exception e) { + System.err.println("Can not parse arguments! See --help"); + System.exit(1); + } + + try { + if (server_type.equals("simple")) { + } else if (server_type.equals("thread-pool")) { + } else if (server_type.equals("nonblocking")) { + if (ssl == true) { + throw new Exception("SSL is not supported over nonblocking servers!"); + } + } else if (server_type.equals("threaded-selector")) { + if (ssl == true) { + throw new Exception("SSL is not supported over nonblocking servers!"); + } + } else { + throw new Exception("Unknown server type! " + server_type); + } + if (protocol_type.equals("binary")) { + } else if (protocol_type.equals("compact")) { + } else if (protocol_type.equals("json")) { + } else if (protocol_type.equals("multi")) { + } else if (protocol_type.equals("multic")) { + } else if (protocol_type.equals("multij")) { + } else { + throw new Exception("Unknown protocol type! " + protocol_type); + } + if (transport_type.equals("buffered")) { + } else if (transport_type.equals("framed")) { + } else if (transport_type.equals("fastframed")) { + } else { + throw new Exception("Unknown transport type! " + transport_type); + } + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + System.exit(1); + } + + // Processors + TestHandler testHandler = new TestHandler(); + ThriftTest.Processor testProcessor = new ThriftTest.Processor(testHandler); + + SecondHandler secondHandler = new SecondHandler(); + SecondService.Processor secondProcessor = new SecondService.Processor(secondHandler); + + // Protocol factory + TProtocolFactory tProtocolFactory = null; + if (protocol_type.equals("json") || protocol_type.equals("multij")) { + tProtocolFactory = new TJSONProtocol.Factory(); + } else if (protocol_type.equals("compact") || protocol_type.equals("multic")) { + tProtocolFactory = new TCompactProtocol.Factory(string_limit, container_limit); + } else { // also covers multi + tProtocolFactory = new TBinaryProtocol.Factory(string_limit, container_limit); + } + + TTransportFactory tTransportFactory = null; + + if (transport_type.equals("framed")) { + tTransportFactory = new TFramedTransport.Factory(); + } else if (transport_type.equals("fastframed")) { + tTransportFactory = new TFastFramedTransport.Factory(); + } else { // .equals("buffered") => default value + tTransportFactory = new TTransportFactory(); + } + + TServer serverEngine = null; + + // If we are multiplexing services in one server... + TMultiplexedProcessor multiplexedProcessor = new TMultiplexedProcessor(); + multiplexedProcessor.registerDefault (testProcessor); + multiplexedProcessor.registerProcessor("ThriftTest", testProcessor); + multiplexedProcessor.registerProcessor("SecondService", secondProcessor); + + if (server_type.equals("nonblocking") || + server_type.equals("threaded-selector")) { + // Nonblocking servers + TNonblockingServerSocket tNonblockingServerSocket = + new TNonblockingServerSocket(new TNonblockingServerSocket.NonblockingAbstractServerSocketArgs().port(port)); + + if (server_type.contains("nonblocking")) { + // Nonblocking Server + TNonblockingServer.Args tNonblockingServerArgs + = new TNonblockingServer.Args(tNonblockingServerSocket); + tNonblockingServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor); + tNonblockingServerArgs.protocolFactory(tProtocolFactory); + tNonblockingServerArgs.transportFactory(tTransportFactory); + serverEngine = new TNonblockingServer(tNonblockingServerArgs); + } else { // server_type.equals("threaded-selector") + // ThreadedSelector Server + TThreadedSelectorServer.Args tThreadedSelectorServerArgs + = new TThreadedSelectorServer.Args(tNonblockingServerSocket); + tThreadedSelectorServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor); + tThreadedSelectorServerArgs.protocolFactory(tProtocolFactory); + tThreadedSelectorServerArgs.transportFactory(tTransportFactory); + serverEngine = new TThreadedSelectorServer(tThreadedSelectorServerArgs); + } + } else { + // Blocking servers + + // SSL socket + TServerSocket tServerSocket = null; + if (ssl) { + tServerSocket = TSSLTransportFactory.getServerSocket(port, 0); + } else { + tServerSocket = new TServerSocket(new TServerSocket.ServerSocketTransportArgs().port(port)); + } + + if (server_type.equals("simple")) { + // Simple Server + TServer.Args tServerArgs = new TServer.Args(tServerSocket); + tServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor); + tServerArgs.protocolFactory(tProtocolFactory); + tServerArgs.transportFactory(tTransportFactory); + serverEngine = new TSimpleServer(tServerArgs); + } else { // server_type.equals("threadpool") + // ThreadPool Server + TThreadPoolServer.Args tThreadPoolServerArgs + = new TThreadPoolServer.Args(tServerSocket); + tThreadPoolServerArgs.processor(protocol_type.startsWith("multi") ? multiplexedProcessor : testProcessor); + tThreadPoolServerArgs.protocolFactory(tProtocolFactory); + tThreadPoolServerArgs.transportFactory(tTransportFactory); + serverEngine = new TThreadPoolServer(tThreadPoolServerArgs); + } + } + + // Set server event handler + serverEngine.setServerEventHandler(new TestServerEventHandler()); + + // Run it + System.out.println("Starting the " + (ssl ? "ssl server" : "server") + + " [" + protocol_type + "/" + transport_type + "/" + server_type + "] on " + + ((domain_socket == "") ? ("port " + port) : ("unix socket " + domain_socket))); + serverEngine.serve(); + + } catch (Exception x) { + x.printStackTrace(); + } + System.out.println("done."); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java new file mode 100644 index 000000000..a0013a93a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/test/WriteStruct.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.test; + +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; + +import org.apache.thrift.Fixtures; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; +import org.apache.thrift.transport.TTransport; + +public class WriteStruct { + public static void main(String[] args) throws Exception { + if (args.length != 2) { + System.out.println("usage: java -cp build/classes org.apache.thrift.test.WriteStruct filename proto_factory_class"); + System.out.println("Write out an instance of Fixtures.compactProtocolTestStruct to 'file'. Use a protocol from 'proto_factory_class'."); + } + + TTransport trans = new TIOStreamTransport(new BufferedOutputStream(new FileOutputStream(args[0]))); + + TProtocolFactory factory = (TProtocolFactory)Class.forName(args[1]).newInstance(); + + TProtocol proto = factory.getProtocol(trans); + + Fixtures.compactProtoTestStruct.write(proto); + trans.flush(); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java new file mode 100644 index 000000000..3c749f9fb --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/ReadCountingTransport.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + + +public class ReadCountingTransport extends TTransport { + public int readCount = 0; + private TTransport trans; + private boolean open = true; + + public ReadCountingTransport(TTransport underlying) { + trans = underlying; + } + + @Override + public void close() { + open = false; + } + + @Override + public boolean isOpen() { + return open; + } + + @Override + public void open() throws TTransportException { + open = true; + } + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + if (!isOpen()) { + throw new TTransportException(TTransportException.NOT_OPEN, "Transport is closed"); + } + readCount++; + return trans.read(buf, off, len); + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + if (!isOpen()) { + throw new TTransportException(TTransportException.NOT_OPEN, "Transport is closed"); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java new file mode 100644 index 000000000..c35348953 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBuffer.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import junit.framework.TestCase; + +public class TestAutoExpandingBuffer extends TestCase { + public void testExpands() throws Exception { + // has expected initial capacity + AutoExpandingBuffer b = new AutoExpandingBuffer(10); + assertEquals(10, b.array().length); + + // doesn't shrink + b.resizeIfNecessary(8); + assertEquals(10, b.array().length); + + // grows when more capacity is needed + b.resizeIfNecessary(100); + assertTrue(b.array().length >= 100); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java new file mode 100644 index 000000000..83ebc2d4c --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferReadTransport.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.nio.ByteBuffer; + +import junit.framework.TestCase; + +public class TestAutoExpandingBufferReadTransport extends TestCase { + private static final byte[] HUNDRED_BYTES = new byte[100]; + + static { + for (byte i = 0; i < 100; i++) { + HUNDRED_BYTES[i] = i; + } + } + + public void testIt() throws Exception { + AutoExpandingBufferReadTransport t = new AutoExpandingBufferReadTransport(150); + + TMemoryInputTransport membuf = new TMemoryInputTransport(HUNDRED_BYTES); + + t.fill(membuf, 100); + assertEquals(100, t.getBytesRemainingInBuffer()); + assertEquals(0, t.getBufferPosition()); + + byte[] target = new byte[10]; + assertEquals(10, t.read(target, 0, 10)); + assertEquals(ByteBuffer.wrap(HUNDRED_BYTES, 0, 10), ByteBuffer.wrap(target)); + + assertEquals(90, t.getBytesRemainingInBuffer()); + assertEquals(10, t.getBufferPosition()); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java new file mode 100644 index 000000000..86b5b0d0f --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestAutoExpandingBufferWriteTransport.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.nio.ByteBuffer; +import org.junit.Test; +import static org.junit.Assert.*; + +public class TestAutoExpandingBufferWriteTransport { + + @Test + public void testIt() throws Exception { + AutoExpandingBufferWriteTransport t = new AutoExpandingBufferWriteTransport(1, 0); + assertEquals(0, t.getLength()); + assertEquals(1, t.getBuf().array().length); + byte[] b1 = new byte[]{1,2,3}; + t.write(b1); + assertEquals(3, t.getLength()); + assertTrue(t.getBuf().array().length >= 3); + assertEquals(ByteBuffer.wrap(b1), ByteBuffer.wrap(t.getBuf().array(), 0, 3)); + + t.reset(); + assertEquals(0, t.getLength()); + assertTrue(t.getBuf().array().length >= 3); + byte[] b2 = new byte[]{4,5}; + t.write(b2); + assertEquals(2, t.getLength()); + assertEquals(ByteBuffer.wrap(b2), ByteBuffer.wrap(t.getBuf().array(), 0, 2)); + + AutoExpandingBufferWriteTransport uut = new AutoExpandingBufferWriteTransport(8, 4); + assertEquals(4, uut.getLength()); + assertEquals(8, uut.getBuf().array().length); + uut.write(b1); + assertEquals(7, uut.getLength()); + assertEquals(8, uut.getBuf().array().length); + assertEquals(ByteBuffer.wrap(b1), ByteBuffer.wrap(uut.getBuf().array(), 4, 3)); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadInitialSize() throws IllegalArgumentException { + new AutoExpandingBufferWriteTransport(0, 0); + } + + @Test(expected = IllegalArgumentException.class) + public void testBadFrontReserveSize() throws IllegalArgumentException { + new AutoExpandingBufferWriteTransport(4, -1); + } + + @Test(expected = IllegalArgumentException.class) + public void testTooSmallFrontReserveSize() throws IllegalArgumentException { + new AutoExpandingBufferWriteTransport(4, 5); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java new file mode 100644 index 000000000..bdc0a848a --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTByteBuffer.java @@ -0,0 +1,36 @@ +package org.apache.thrift.transport; + +import junit.framework.TestCase; +import java.nio.charset.StandardCharsets; +import org.apache.thrift.TException; + +import java.nio.ByteBuffer; + +public class TestTByteBuffer extends TestCase { + public void testReadWrite() throws Exception { + final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(16)); + byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8)); + assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8)); + } + + public void testReuseReadWrite() throws Exception { + final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(16)); + byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8)); + assertEquals("Hello World", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8)); + + byteBuffer.clear(); + + byteBuffer.write("Goodbye Horses".getBytes(StandardCharsets.UTF_8)); + assertEquals("Goodbye Horses", new String(byteBuffer.flip().toByteArray(), StandardCharsets.UTF_8)); + } + + public void testOverflow() throws Exception { + final TByteBuffer byteBuffer = new TByteBuffer(ByteBuffer.allocate(4)); + try { + byteBuffer.write("Hello World".getBytes(StandardCharsets.UTF_8)); + fail("Expected write operation to fail with TTransportException"); + } catch (TTransportException e) { + assertEquals("Not enough room in output buffer", e.getMessage()); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java new file mode 100644 index 000000000..06ee20666 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFastFramedTransport.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +public class TestTFastFramedTransport extends TestTFramedTransport { + protected final static int INITIAL_CAPACITY = 50; + + @Override + protected TTransport getTransport(TTransport underlying) { + return new TFastFramedTransport(underlying, INITIAL_CAPACITY, 10 * 1024 * 1024); + } + + @Override + protected TTransport getTransport(TTransport underlying, int maxLength) { + return new TFastFramedTransport(underlying, INITIAL_CAPACITY, maxLength); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java new file mode 100644 index 000000000..e30d74b07 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTFramedTransport.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import junit.framework.TestCase; + +public class TestTFramedTransport extends TestCase { + + protected TTransport getTransport(TTransport underlying) { + return new TFramedTransport(underlying); + } + + protected TTransport getTransport(TTransport underlying, int maxLength) { + return new TFramedTransport(underlying, maxLength); + } + + public static byte[] byteSequence(int start, int end) { + byte[] result = new byte[end-start+1]; + for (int i = 0; i <= (end-start); i++) { + result[i] = (byte)(start+i); + } + return result; + } + + public void testRead() throws IOException, TTransportException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(50); + dos.write(byteSequence(0, 49)); + + dos.writeInt(220); + dos.write(byteSequence(0, 219)); + + TMemoryBuffer membuf = new TMemoryBuffer(0); + membuf.write(baos.toByteArray()); + + ReadCountingTransport countTrans = new ReadCountingTransport(membuf); + TTransport trans = getTransport(countTrans); + + byte[] readBuf = new byte[10]; + trans.read(readBuf, 0, 10); + assertTrue(Arrays.equals(readBuf, byteSequence(0,9))); + assertEquals(2, countTrans.readCount); + + trans.read(readBuf, 0, 10); + assertTrue(Arrays.equals(readBuf, byteSequence(10,19))); + assertEquals(2, countTrans.readCount); + + assertEquals(30, trans.read(new byte[30], 0, 30)); + assertEquals(2, countTrans.readCount); + + readBuf = new byte[220]; + assertEquals(220, trans.read(readBuf, 0, 220)); + assertTrue(Arrays.equals(readBuf, byteSequence(0, 219))); + assertEquals(4, countTrans.readCount); + } + + public void testInvalidFrameSize() throws IOException, TTransportException { + int maxLength = 128; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(130); + dos.write(byteSequence(0, 129)); + + TMemoryBuffer membuf = new TMemoryBuffer(0); + membuf.write(baos.toByteArray()); + + ReadCountingTransport countTrans = new ReadCountingTransport(membuf); + TTransport trans = getTransport(countTrans, maxLength); + + byte[] readBuf = new byte[10]; + try { + trans.read(readBuf, 0, 4); + fail("Expected a TTransportException"); + } catch (TTransportException e) { + // We expect this exception because the frame we're trying to read is larger than our max frame length + assertEquals(TTransportException.CORRUPTED_DATA, e.getType()); + } + + assertFalse(trans.isOpen()); + + try { + trans.read(readBuf, 0, 4); + fail("Expected a TTransportException"); + } catch (TTransportException e) { + // This time we get an exception indicating the connection was closed + assertEquals(TTransportException.NOT_OPEN, e.getType()); + } + } + + public void testWrite() throws TTransportException, IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + WriteCountingTransport countingTrans = new WriteCountingTransport(new TIOStreamTransport(new BufferedOutputStream(baos))); + TTransport trans = getTransport(countingTrans); + + trans.write(byteSequence(0,100)); + assertEquals(0, countingTrans.writeCount); + trans.write(byteSequence(101,200)); + trans.write(byteSequence(201,255)); + assertEquals(0, countingTrans.writeCount); + + trans.flush(); + assertEquals(1, countingTrans.writeCount); + + trans.write(byteSequence(0, 245)); + trans.flush(); + assertEquals(2, countingTrans.writeCount); + + DataInputStream din = new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); + assertEquals(256, din.readInt()); + + byte[] buf = new byte[256]; + din.read(buf, 0, 256); + assertTrue(Arrays.equals(byteSequence(0,255), buf)); + + assertEquals(246, din.readInt()); + buf = new byte[246]; + din.read(buf, 0, 246); + assertTrue(Arrays.equals(byteSequence(0,245), buf)); + } + + public void testDirectRead() throws IOException, TTransportException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(50); + dos.write(byteSequence(0, 49)); + dos.writeInt(75); + dos.write(byteSequence(125, 200)); + + TMemoryBuffer membuf = new TMemoryBuffer(0); + membuf.write(baos.toByteArray()); + + ReadCountingTransport countTrans = new ReadCountingTransport(membuf); + TTransport trans = getTransport(countTrans); + + assertEquals(0, trans.getBytesRemainingInBuffer()); + + byte[] readBuf = new byte[10]; + trans.read(readBuf, 0, 10); + assertTrue(Arrays.equals(readBuf, byteSequence(0,9))); + + assertEquals(40, trans.getBytesRemainingInBuffer()); + assertEquals(10, trans.getBufferPosition()); + + trans.consumeBuffer(5); + assertEquals(35, trans.getBytesRemainingInBuffer()); + assertEquals(15, trans.getBufferPosition()); + + assertEquals(2, countTrans.readCount); + + assertEquals(35, trans.read(new byte[35], 0, 35)); + assertEquals(0, trans.getBytesRemainingInBuffer()); + assertEquals(50, trans.getBufferPosition()); + + trans.read(readBuf, 0, 10); + assertEquals(4, countTrans.readCount); + assertTrue(Arrays.equals(readBuf, byteSequence(125,134))); + assertEquals(65, trans.getBytesRemainingInBuffer()); + assertEquals(10, trans.getBufferPosition()); + } + + public void testClear() throws IOException, TTransportException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(220); + dos.write(byteSequence(0, 219)); + + TMemoryBuffer membuf = new TMemoryBuffer(0); + membuf.write(baos.toByteArray()); + + ReadCountingTransport countTrans = new ReadCountingTransport(membuf); + TTransport trans = getTransport(countTrans); + + byte[] readBuf = new byte[220]; + trans.read(readBuf, 0, 220); + assertTrue(Arrays.equals(readBuf, byteSequence(0,219))); + + assertTrue(trans instanceof TFramedTransport || trans instanceof TFastFramedTransport); + if (trans instanceof TFramedTransport) { + assertTrue(trans.getBuffer() != null && trans.getBuffer().length > 0); + ((TFramedTransport) trans).clear(); + assertTrue(trans.getBuffer() == null); + } else if (trans instanceof TFastFramedTransport) { + assertTrue(trans.getBuffer().length > TestTFastFramedTransport.INITIAL_CAPACITY); + ((TFastFramedTransport) trans).clear(); + assertTrue(trans.getBuffer().length == TestTFastFramedTransport.INITIAL_CAPACITY); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java new file mode 100644 index 000000000..273145bd2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTMemoryInputTransport.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.util.Arrays; + +import junit.framework.TestCase; + +public class TestTMemoryInputTransport extends TestCase { + public void testFresh() throws Exception { + byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TMemoryInputTransport trans = new TMemoryInputTransport(input_buf); + assertEquals(0, trans.getBufferPosition()); + assertEquals(input_buf, trans.getBuffer()); + assertEquals(10, trans.getBytesRemainingInBuffer()); + + byte[] buf1 = new byte[4]; + trans.readAll(buf1, 0, 4); + assertTrue(Arrays.equals(new byte[]{1, 2, 3, 4}, buf1)); + assertEquals(4, trans.getBufferPosition()); + assertEquals(6, trans.getBytesRemainingInBuffer()); + + trans.consumeBuffer(2); + + assertEquals(6, trans.getBufferPosition()); + assertEquals(4, trans.getBytesRemainingInBuffer()); + + trans.readAll(buf1, 0, 4); + assertTrue(Arrays.equals(new byte[]{7, 8, 9, 10}, buf1)); + assertEquals(10, trans.getBufferPosition()); + assertEquals(0, trans.getBytesRemainingInBuffer()); + } + + public void testReused() throws Exception { + byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TMemoryInputTransport trans = new TMemoryInputTransport(input_buf); + assertEquals(0, trans.getBufferPosition()); + assertEquals(input_buf, trans.getBuffer()); + assertEquals(10, trans.getBytesRemainingInBuffer()); + + byte[] new_buf = new byte[]{10, 9, 8}; + trans.reset(new_buf); + assertEquals(0, trans.getBufferPosition()); + assertEquals(new_buf, trans.getBuffer()); + assertEquals(3, trans.getBytesRemainingInBuffer()); + } + + public void testWithOffsetAndLength() throws Exception { + byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TMemoryInputTransport trans = new TMemoryInputTransport(input_buf, 1, 3); + assertEquals(1, trans.getBufferPosition()); + assertEquals(3, trans.getBytesRemainingInBuffer()); + byte[] readBuffer = new byte[3]; + trans.readAll(readBuffer, 0, 3); + assertTrue(Arrays.equals(new byte[]{2, 3, 4}, readBuffer)); + + try { + assertEquals(0, trans.readAll(readBuffer, 0, 3)); + fail("should have thrown an exception"); + } catch (Exception e) { + // yay + } + + trans.reset(input_buf, 3, 4); + readBuffer = new byte[4]; + trans.readAll(readBuffer, 0, 4); + assertTrue(Arrays.equals(new byte[]{4, 5, 6, 7}, readBuffer)); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java new file mode 100644 index 000000000..032c2eb71 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.ServerTestBase; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.TSimpleServer; +import org.apache.thrift.server.TServer.Args; + +public class TestTSSLTransportFactory extends ServerTestBase { + private Thread serverThread; + private TServer server; + + private static final List<TProtocolFactory> protocols = new ArrayList<TProtocolFactory>(); + static { + // TODO: Only supported on TBinaryProtocol. Doesn't work for TCompactProtocol + protocols.add(new TBinaryProtocol.Factory()); + } + + @Override + public TTransport getClientTransport(TTransport underlyingTransport) + throws Exception { + return TSSLTransportFactory.getClientSocket(HOST, PORT); + } + + protected TServerSocket getServerTransport() throws Exception { + return TSSLTransportFactory.getServerSocket(PORT); + } + + @Override + public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) + throws Exception { + serverThread = new Thread() { + public void run() { + try { + TServerTransport serverTransport = getServerTransport(); + final Args args = new Args(serverTransport).processor(processor); + server = new TSimpleServer(args); + server.serve(); + } catch (Exception e) { + e.printStackTrace(); + assert false; + } + } + }; + + serverThread.start(); + Thread.sleep(SLEEP_DELAY); + } + + @Override + public void stopServer() throws Exception { + server.stop(); + serverThread.join(); + } + + @Override + public void open(TTransport transport) throws Exception {} + + @Override + public List<TProtocolFactory> getProtocols() { + return protocols; + } + + @Override + public void testTransportFactory() throws Exception { + // this test doesn't really apply to this suite, so let's skip it. + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java new file mode 100644 index 000000000..da1659f9d --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient1.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +public class TestTSSLTransportFactoryCustomClient1 extends TestTSSLTransportFactory { + + @Override + public TTransport getClientTransport(TTransport underlyingTransport) + throws Exception { + TSSLTransportFactory.TSSLTransportParameters params = new + TSSLTransportFactory.TSSLTransportParameters(); + + params.setTrustStore(System.getProperty("javax.net.ssl.trustStore"), + System.getProperty("javax.net.ssl.trustStorePassword")); + + return TSSLTransportFactory.getClientSocket(HOST, PORT, 0/*timeout*/, params); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java new file mode 100644 index 000000000..eaed46057 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryCustomClient2.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +public class TestTSSLTransportFactoryCustomClient2 extends TestTSSLTransportFactory { + + @Override + public TTransport getClientTransport(TTransport underlyingTransport) + throws Exception { + TSSLTransportFactory.TSSLTransportParameters params = new + TSSLTransportFactory.TSSLTransportParameters(); + + params.setTrustStore(System.getProperty("javax.net.ssl.trustStore"), null); + + return TSSLTransportFactory.getClientSocket(HOST, PORT, 0/*timeout*/, params); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java new file mode 100644 index 000000000..25bf5cebb --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactoryStreamedStore.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.FileInputStream; +import java.net.InetAddress; + +public class TestTSSLTransportFactoryStreamedStore extends TestTSSLTransportFactory { + private static String keyStoreLocation = System.getProperty("javax.net.ssl.keyStore"); + private static String trustStoreLocation = System.getProperty("javax.net.ssl.trustStore"); + + public TestTSSLTransportFactoryStreamedStore() { + super(); + + /** + * Override system properties to be able to test passing + * the trustStore and keyStore as input stream + */ + System.setProperty("javax.net.ssl.trustStore", ""); + System.setProperty("javax.net.ssl.keyStore", ""); + } + + @Override + public TTransport getClientTransport(TTransport underlyingTransport) + throws Exception { + TSSLTransportFactory.TSSLTransportParameters params = new + TSSLTransportFactory.TSSLTransportParameters(); + + params.setTrustStore(new FileInputStream(trustStoreLocation), + System.getProperty("javax.net.ssl.trustStorePassword")); + + return TSSLTransportFactory.getClientSocket(HOST, PORT, 0/*timeout*/, params); + } + + @Override + protected TServerSocket getServerTransport() throws Exception { + TSSLTransportFactory.TSSLTransportParameters params = new + TSSLTransportFactory.TSSLTransportParameters(); + + params.setKeyStore(new FileInputStream(keyStoreLocation), + System.getProperty("javax.net.ssl.keyStorePassword")); + + return TSSLTransportFactory.getServerSocket(PORT, 0/*timeout*/, InetAddress.getByName(HOST), params); + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java new file mode 100644 index 000000000..36a06e9e5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java @@ -0,0 +1,471 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslServerFactory; + +import junit.framework.TestCase; + +import org.apache.thrift.TProcessor; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.server.ServerTestBase; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.TSimpleServer; +import org.apache.thrift.server.TServer.Args; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestTSaslTransports extends TestCase { + + private static final Logger LOGGER = LoggerFactory.getLogger(TestTSaslTransports.class); + + private static final String HOST = "localhost"; + private static final String SERVICE = "thrift-test"; + private static final String PRINCIPAL = "thrift-test-principal"; + private static final String PASSWORD = "super secret password"; + private static final String REALM = "thrift-test-realm"; + + private static final String UNWRAPPED_MECHANISM = "CRAM-MD5"; + private static final Map<String, String> UNWRAPPED_PROPS = null; + + private static final String WRAPPED_MECHANISM = "DIGEST-MD5"; + private static final Map<String, String> WRAPPED_PROPS = new HashMap<String, String>(); + + static { + WRAPPED_PROPS.put(Sasl.QOP, "auth-int"); + WRAPPED_PROPS.put("com.sun.security.sasl.digest.realm", REALM); + } + + private static final String testMessage1 = "Hello, world! Also, four " + + "score and seven years ago our fathers brought forth on this " + + "continent a new nation, conceived in liberty, and dedicated to the " + + "proposition that all men are created equal."; + + private static final String testMessage2 = "I have a dream that one day " + + "this nation will rise up and live out the true meaning of its creed: " + + "'We hold these truths to be self-evident, that all men are created equal.'"; + + + private static class TestSaslCallbackHandler implements CallbackHandler { + private final String password; + + public TestSaslCallbackHandler(String password) { + this.password = password; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback c : callbacks) { + if (c instanceof NameCallback) { + ((NameCallback) c).setName(PRINCIPAL); + } else if (c instanceof PasswordCallback) { + ((PasswordCallback) c).setPassword(password.toCharArray()); + } else if (c instanceof AuthorizeCallback) { + ((AuthorizeCallback) c).setAuthorized(true); + } else if (c instanceof RealmCallback) { + ((RealmCallback) c).setText(REALM); + } else { + throw new UnsupportedCallbackException(c); + } + } + } + } + + private class ServerThread extends Thread { + final String mechanism; + final Map<String, String> props; + volatile Throwable thrown; + + public ServerThread(String mechanism, Map<String, String> props) { + this.mechanism = mechanism; + this.props = props; + } + + public void run() { + try { + internalRun(); + } catch (Throwable t) { + thrown = t; + } + } + + private void internalRun() throws Exception { + TServerSocket serverSocket = new TServerSocket( + new TServerSocket.ServerSocketTransportArgs(). + port(ServerTestBase.PORT)); + try { + acceptAndWrite(serverSocket); + } finally { + serverSocket.close(); + } + } + + private void acceptAndWrite(TServerSocket serverSocket) + throws Exception { + TTransport serverTransport = serverSocket.accept(); + TTransport saslServerTransport = new TSaslServerTransport( + mechanism, SERVICE, HOST, + props, new TestSaslCallbackHandler(PASSWORD), serverTransport); + + saslServerTransport.open(); + + byte[] inBuf = new byte[testMessage1.getBytes().length]; + // Deliberately read less than the full buffer to ensure + // that TSaslTransport is correctly buffering reads. This + // will fail for the WRAPPED test, if it doesn't work. + saslServerTransport.readAll(inBuf, 0, 5); + saslServerTransport.readAll(inBuf, 5, 10); + saslServerTransport.readAll(inBuf, 15, inBuf.length - 15); + LOGGER.debug("server got: {}", new String(inBuf)); + assertEquals(new String(inBuf), testMessage1); + + LOGGER.debug("server writing: {}", testMessage2); + saslServerTransport.write(testMessage2.getBytes()); + saslServerTransport.flush(); + + saslServerTransport.close(); + } + } + + private void testSaslOpen(final String mechanism, final Map<String, String> props) + throws Exception { + ServerThread serverThread = new ServerThread(mechanism, props); + serverThread.start(); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ah well. + } + + try { + TSocket clientSocket = new TSocket(HOST, ServerTestBase.PORT); + TTransport saslClientTransport = new TSaslClientTransport(mechanism, + PRINCIPAL, SERVICE, HOST, props, new TestSaslCallbackHandler(PASSWORD), clientSocket); + saslClientTransport.open(); + LOGGER.debug("client writing: {}", testMessage1); + saslClientTransport.write(testMessage1.getBytes()); + saslClientTransport.flush(); + + byte[] inBuf = new byte[testMessage2.getBytes().length]; + saslClientTransport.readAll(inBuf, 0, inBuf.length); + LOGGER.debug("client got: {}", new String(inBuf)); + assertEquals(new String(inBuf), testMessage2); + + TTransportException expectedException = null; + try { + saslClientTransport.open(); + } catch (TTransportException e) { + expectedException = e; + } + assertNotNull(expectedException); + + saslClientTransport.close(); + } catch (Exception e) { + LOGGER.warn("Exception caught", e); + throw e; + } finally { + serverThread.interrupt(); + try { + serverThread.join(); + } catch (InterruptedException e) { + // Ah well. + } + assertNull(serverThread.thrown); + } + } + + public void testUnwrappedOpen() throws Exception { + testSaslOpen(UNWRAPPED_MECHANISM, UNWRAPPED_PROPS); + } + + public void testWrappedOpen() throws Exception { + testSaslOpen(WRAPPED_MECHANISM, WRAPPED_PROPS); + } + + public void testAnonymousOpen() throws Exception { + testSaslOpen("ANONYMOUS", null); + } + + /** + * Test that we get the proper exceptions thrown back the server when + * the client provides invalid password. + */ + public void testBadPassword() throws Exception { + ServerThread serverThread = new ServerThread(UNWRAPPED_MECHANISM, UNWRAPPED_PROPS); + serverThread.start(); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ah well. + } + + boolean clientSidePassed = true; + + try { + TSocket clientSocket = new TSocket(HOST, ServerTestBase.PORT); + TTransport saslClientTransport = new TSaslClientTransport( + UNWRAPPED_MECHANISM, PRINCIPAL, SERVICE, HOST, UNWRAPPED_PROPS, + new TestSaslCallbackHandler("NOT THE PASSWORD"), clientSocket); + saslClientTransport.open(); + clientSidePassed = false; + fail("Was able to open transport with bad password"); + } catch (TTransportException tte) { + LOGGER.error("Exception for bad password", tte); + assertNotNull(tte.getMessage()); + assertTrue(tte.getMessage().contains("Invalid response")); + + } finally { + serverThread.interrupt(); + serverThread.join(); + + if (clientSidePassed) { + assertNotNull(serverThread.thrown); + assertTrue(serverThread.thrown.getMessage().contains("Invalid response")); + } + } + } + + public void testWithServer() throws Exception { + new TestTSaslTransportsWithServer().testIt(); + } + + private static class TestTSaslTransportsWithServer extends ServerTestBase { + + private Thread serverThread; + private TServer server; + + @Override + public TTransport getClientTransport(TTransport underlyingTransport) throws Exception { + return new TSaslClientTransport( + WRAPPED_MECHANISM, PRINCIPAL, SERVICE, HOST, WRAPPED_PROPS, + new TestSaslCallbackHandler(PASSWORD), underlyingTransport); + } + + @Override + public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception { + serverThread = new Thread() { + public void run() { + try { + // Transport + TServerSocket socket = new TServerSocket(new TServerSocket.ServerSocketTransportArgs().port(PORT)); + + TTransportFactory factory = new TSaslServerTransport.Factory( + WRAPPED_MECHANISM, SERVICE, HOST, WRAPPED_PROPS, + new TestSaslCallbackHandler(PASSWORD)); + server = new TSimpleServer(new Args(socket).processor(processor).transportFactory(factory).protocolFactory(protoFactory)); + + // Run it + LOGGER.debug("Starting the server on port {}", PORT); + server.serve(); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + }; + serverThread.start(); + Thread.sleep(1000); + } + + @Override + public void stopServer() throws Exception { + server.stop(); + try { + serverThread.join(); + } catch (InterruptedException e) {} + } + + } + + + /** + * Implementation of SASL ANONYMOUS, used for testing client-side + * initial responses. + */ + private static class AnonymousClient implements SaslClient { + private final String username; + private boolean hasProvidedInitialResponse; + + public AnonymousClient(String username) { + this.username = username; + } + + public String getMechanismName() { return "ANONYMOUS"; } + public boolean hasInitialResponse() { return true; } + public byte[] evaluateChallenge(byte[] challenge) throws SaslException { + if (hasProvidedInitialResponse) { + throw new SaslException("Already complete!"); + } + + hasProvidedInitialResponse = true; + return username.getBytes(StandardCharsets.UTF_8); + } + public boolean isComplete() { return hasProvidedInitialResponse; } + public byte[] unwrap(byte[] incoming, int offset, int len) { + throw new UnsupportedOperationException(); + } + public byte[] wrap(byte[] outgoing, int offset, int len) { + throw new UnsupportedOperationException(); + } + public Object getNegotiatedProperty(String propName) { return null; } + public void dispose() {} + } + + private static class AnonymousServer implements SaslServer { + private String user; + public String getMechanismName() { return "ANONYMOUS"; } + public byte[] evaluateResponse(byte[] response) throws SaslException { + this.user = new String(response, StandardCharsets.UTF_8); + return null; + } + public boolean isComplete() { return user != null; } + public String getAuthorizationID() { return user; } + public byte[] unwrap(byte[] incoming, int offset, int len) { + throw new UnsupportedOperationException(); + } + public byte[] wrap(byte[] outgoing, int offset, int len) { + throw new UnsupportedOperationException(); + } + public Object getNegotiatedProperty(String propName) { return null; } + public void dispose() {} + + } + + public static class SaslAnonymousFactory + implements SaslClientFactory, SaslServerFactory { + + public SaslClient createSaslClient( + String[] mechanisms, String authorizationId, String protocol, + String serverName, Map<String,?> props, CallbackHandler cbh) + { + for (String mech : mechanisms) { + if ("ANONYMOUS".equals(mech)) { + return new AnonymousClient(authorizationId); + } + } + return null; + } + + public SaslServer createSaslServer( + String mechanism, String protocol, String serverName, Map<String,?> props, CallbackHandler cbh) + { + if ("ANONYMOUS".equals(mechanism)) { + return new AnonymousServer(); + } + return null; + } + public String[] getMechanismNames(Map<String, ?> props) { + return new String[] { "ANONYMOUS" }; + } + } + + static { + java.security.Security.addProvider(new SaslAnonymousProvider()); + } + public static class SaslAnonymousProvider extends java.security.Provider { + public SaslAnonymousProvider() { + super("ThriftSaslAnonymous", 1.0, "Thrift Anonymous SASL provider"); + put("SaslClientFactory.ANONYMOUS", SaslAnonymousFactory.class.getName()); + put("SaslServerFactory.ANONYMOUS", SaslAnonymousFactory.class.getName()); + } + } + + private static class MockTTransport extends TTransport { + + byte[] badHeader = null; + private TMemoryInputTransport readBuffer = new TMemoryInputTransport(); + + public MockTTransport(int mode) { + if (mode==1) { + // Invalid status byte + badHeader = new byte[] { (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05 }; + } else if (mode == 2) { + // Valid status byte, negative payload length + badHeader = new byte[] { (byte)0x01, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF }; + } else if (mode == 3) { + // Valid status byte, excessively large, bogus payload length + badHeader = new byte[] { (byte)0x01, (byte)0x64, (byte)0x00, (byte)0x00, (byte)0x00 }; + } + readBuffer.reset(badHeader); + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + public void open() throws TTransportException {} + + @Override + public void close() {} + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + return readBuffer.read(buf, off, len); + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException {} + } + + public void testBadHeader() { + TSaslTransport saslTransport = new TSaslServerTransport(new MockTTransport(1)); + try { + saslTransport.receiveSaslMessage(); + fail("Should have gotten an error due to incorrect status byte value."); + } catch (TTransportException e) { + assertEquals(e.getMessage(), "Invalid status -1"); + } + saslTransport = new TSaslServerTransport(new MockTTransport(2)); + try { + saslTransport.receiveSaslMessage(); + fail("Should have gotten an error due to negative payload length."); + } catch (TTransportException e) { + assertEquals(e.getMessage(), "Invalid payload header length: -1"); + } + saslTransport = new TSaslServerTransport(new MockTTransport(3)); + try { + saslTransport.receiveSaslMessage(); + fail("Should have gotten an error due to bogus (large) payload length."); + } catch (TTransportException e) { + assertEquals(e.getMessage(), "Invalid payload header length: 1677721600"); + } + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java new file mode 100644 index 000000000..7b880f499 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTSimpleFileTransport.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import junit.framework.TestCase; + +public class TestTSimpleFileTransport extends TestCase { + public void testFresh() throws Exception { + //Test write side + Path tempFilePathName = Files.createTempFile("TSimpleFileTransportTest", null); + Files.delete(tempFilePathName); + byte[] input_buf = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + TSimpleFileTransport trans_write = new TSimpleFileTransport(tempFilePathName.toString(),false, true, false); + assert (!trans_write.isOpen()); + trans_write.open(); + assert(trans_write.isOpen()); + trans_write.write(input_buf); + trans_write.write(input_buf,2,2); + trans_write.flush(); + trans_write.close(); + + //Test read side + TSimpleFileTransport trans = new TSimpleFileTransport(tempFilePathName.toString(),true, false); + assert(trans.isOpen()); + + //Simple file trans provides no buffer access + assert(0 == trans.getBufferPosition()); + assert(null == trans.getBuffer()); + assert(-1 == trans.getBytesRemainingInBuffer()); + + //Test file pointer operations + assert(0 == trans.getFilePointer()); + assert(12 == trans.length()); + + final int BUFSIZ = 4; + byte[] buf1 = new byte[BUFSIZ]; + trans.readAll(buf1, 0, BUFSIZ); + assert(BUFSIZ == trans.getFilePointer()); + assert(Arrays.equals(new byte[]{1, 2, 3, 4}, buf1)); + + int bytesRead = trans.read(buf1, 0, BUFSIZ); + assert(bytesRead > 0); + for (int i = 0; i < bytesRead; ++i) { + assert(buf1[i] == i+5); + } + + trans.seek(0); + assert(0 == trans.getFilePointer()); + trans.readAll(buf1, 0, BUFSIZ); + assert(Arrays.equals(new byte[]{1, 2, 3, 4}, buf1)); + assert(BUFSIZ == trans.getFilePointer()); + trans.close(); + Files.delete(tempFilePathName); + } +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java new file mode 100644 index 000000000..3d7f9c1c9 --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/TestTZlibTransport.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.zip.DataFormatException; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +import junit.framework.TestCase; + +public class TestTZlibTransport extends TestCase { + + protected TTransport getTransport(TTransport underlying) { + return new TZlibTransport(underlying); + } + + public static byte[] byteSequence(int start, int end) { + byte[] result = new byte[end-start+1]; + for (int i = 0; i <= (end-start); i++) { + result[i] = (byte)(start+i); + } + return result; + } + + public void testClose() throws TTransportException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + WriteCountingTransport countingTrans = new WriteCountingTransport(new TIOStreamTransport(new BufferedOutputStream + (baos))); + TTransport trans = getTransport(countingTrans); + trans.write(byteSequence(0, 245)); + countingTrans.close(); + trans.close(); + } + + public void testCloseOpen() throws TTransportException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + TTransport trans = getTransport(new TIOStreamTransport(baos)); + byte[] uncompressed = byteSequence(0, 245); + trans.write(uncompressed); + trans.close(); + final byte[] compressed = baos.toByteArray(); + + final byte[] buf = new byte[255]; + TTransport transRead = getTransport(new TIOStreamTransport(new ByteArrayInputStream(compressed))); + int readBytes = transRead.read(buf, 0, buf.length); + assertEquals(uncompressed.length, readBytes); + transRead.close(); + } + + public void testRead() throws IOException, TTransportException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(baos); + DataOutputStream dos = new DataOutputStream(deflaterOutputStream); + dos.write(byteSequence(0, 49)); + dos.write(byteSequence(0, 219)); + + deflaterOutputStream.finish(); + + TMemoryBuffer membuf = new TMemoryBuffer(0); + membuf.write(baos.toByteArray()); + + ReadCountingTransport countTrans = new ReadCountingTransport(membuf); + TTransport trans = getTransport(countTrans); + + byte[] readBuf = new byte[10]; + trans.read(readBuf, 0, 10); + assertTrue(Arrays.equals(readBuf, byteSequence(0,9))); + assertEquals(1, countTrans.readCount); + + trans.read(readBuf, 0, 10); + assertTrue(Arrays.equals(readBuf, byteSequence(10,19))); + assertEquals(1, countTrans.readCount); + + assertEquals(30, trans.read(new byte[30], 0, 30)); + assertEquals(1, countTrans.readCount); + + readBuf = new byte[220]; + assertEquals(220, trans.read(readBuf, 0, 220)); + assertTrue(Arrays.equals(readBuf, byteSequence(0, 219))); + assertEquals(1, countTrans.readCount); + } + + public void testWrite() throws TTransportException, IOException, DataFormatException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + WriteCountingTransport countingTrans = new WriteCountingTransport(new TIOStreamTransport(new BufferedOutputStream(baos))); + TTransport trans = getTransport(countingTrans); + + trans.write(byteSequence(0, 100)); + assertEquals(1, countingTrans.writeCount); + trans.write(byteSequence(101, 200)); + trans.write(byteSequence(201, 255)); + assertEquals(1, countingTrans.writeCount); + + trans.flush(); + assertEquals(2, countingTrans.writeCount); + + trans.write(byteSequence(0, 245)); + trans.flush(); + assertEquals(3, countingTrans.writeCount); + + DataInputStream din = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray()))); + byte[] buf = new byte[256]; + int n = din.read(buf, 0, 256); + assertEquals(n, 256); + assertTrue(Arrays.equals(byteSequence(0, 255), buf)); + + buf = new byte[246]; + n = din.read(buf, 0, 246); + assertEquals(n, 246); + for (int i = 0; i<buf.length; i++) { + assertEquals("for "+i, byteSequence(0,245)[i], buf[i]); + } + + assertTrue(Arrays.equals(byteSequence(0,245), buf)); + } + +} diff --git a/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java new file mode 100644 index 000000000..358f5c6eb --- /dev/null +++ b/src/jaegertracing/thrift/lib/java/test/org/apache/thrift/transport/WriteCountingTransport.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + + +public class WriteCountingTransport extends TTransport { + public int writeCount = 0; + private final TTransport trans; + + public WriteCountingTransport(TTransport underlying) { + trans = underlying; + } + + @Override + public void close() {} + + @Override + public boolean isOpen() {return true;} + + @Override + public void open() throws TTransportException {} + + @Override + public int read(byte[] buf, int off, int len) throws TTransportException { + return 0; + } + + @Override + public void write(byte[] buf, int off, int len) throws TTransportException { + writeCount ++; + trans.write(buf, off, len); + } + + @Override + public void flush() throws TTransportException { + trans.flush(); + } +}
\ No newline at end of file diff --git a/src/jaegertracing/thrift/lib/javame/coding_standards.md b/src/jaegertracing/thrift/lib/javame/coding_standards.md new file mode 100644 index 000000000..fa0390bb5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/coding_standards.md @@ -0,0 +1 @@ +Please follow [General Coding Standards](/doc/coding_standards.md) diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java new file mode 100644 index 000000000..2f8612af3 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TApplicationException.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolUtil; +import org.apache.thrift.protocol.TStruct; +import org.apache.thrift.protocol.TType; + +/** + * Application level exception + * + */ +public class TApplicationException extends TException { + + private static final long serialVersionUID = 1L; + + public static final int UNKNOWN = 0; + public static final int UNKNOWN_METHOD = 1; + public static final int INVALID_MESSAGE_TYPE = 2; + public static final int WRONG_METHOD_NAME = 3; + public static final int BAD_SEQUENCE_ID = 4; + public static final int MISSING_RESULT = 5; + public static final int INTERNAL_ERROR = 6; + public static final int PROTOCOL_ERROR = 7; + public static final int INVALID_TRANSFORM = 8; + public static final int INVALID_PROTOCOL = 9; + public static final int UNSUPPORTED_CLIENT_TYPE = 10; + + protected int type_ = UNKNOWN; + + public TApplicationException() { + super(); + } + + public TApplicationException(int type) { + super(); + type_ = type; + } + + public TApplicationException(int type, String message) { + super(message); + type_ = type; + } + + public TApplicationException(String message) { + super(message); + } + + public int getType() { + return type_; + } + + public static TApplicationException read(TProtocol iprot) throws TException { + TField field; + iprot.readStructBegin(); + + String message = null; + int type = UNKNOWN; + + while (true) { + field = iprot.readFieldBegin(); + if (field.type == TType.STOP) { + break; + } + switch (field.id) { + case 1: + if (field.type == TType.STRING) { + message = iprot.readString(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + case 2: + if (field.type == TType.I32) { + type = iprot.readI32(); + } else { + TProtocolUtil.skip(iprot, field.type); + } + break; + default: + TProtocolUtil.skip(iprot, field.type); + break; + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + return new TApplicationException(type, message); + } + + public void write(TProtocol oprot) throws TException { + TStruct struct = new TStruct("TApplicationException"); + TField field = new TField(); + oprot.writeStructBegin(struct); + if (getMessage() != null) { + field.name = "message"; + field.type = TType.STRING; + field.id = 1; + oprot.writeFieldBegin(field); + oprot.writeString(getMessage()); + oprot.writeFieldEnd(); + } + field.name = "type"; + field.type = TType.I32; + field.id = 2; + oprot.writeFieldBegin(field); + oprot.writeI32(type_); + oprot.writeFieldEnd(); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java new file mode 100644 index 000000000..13ea024e2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBase.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TProtocol; + +/** + * Generic base interface for generated Thrift objects. + * + */ +public interface TBase { + + /** + * Reads the TObject from the given input protocol. + * + * @param iprot Input protocol + */ + public void read(TProtocol iprot) throws TException; + + /** + * Writes the objects out to the protocol + * + * @param oprot Output protocol + */ + public void write(TProtocol oprot) throws TException; + + public int compareTo(Object other); + +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java new file mode 100644 index 000000000..2f31d311c --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TBaseHelper.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.util.Vector; +import java.util.Hashtable; +import java.util.Enumeration; + +public class TBaseHelper { + + public static int compareTo(boolean a, boolean b) { + return (a == b) ? 0 : (a ? 1 : -1); + + + } + public static int compareTo(Boolean a, Boolean b) { + return (a.booleanValue() == b.booleanValue()) ? 0 : (a.booleanValue() ? 1 : -1); + + + } + public static int compareTo(Boolean a, boolean b) { + return (a.booleanValue() == b) ? 0 : (a.booleanValue() ? 1 : -1); + + + } + + public static Boolean booleanValueOf(boolean b) { + return (b ? Boolean.TRUE : Boolean.FALSE); + } + + public static int compareTo(byte a, byte b) { + if (a < b) { + return -1; + } else if (b < a) { + return 1; + } else { + return 0; + } + } + + public static int compareTo(short a, short b) { + if (a < b) { + return -1; + } else if (b < a) { + return 1; + } else { + return 0; + } + } + + public static int compareTo(int a, int b) { + if (a < b) { + return -1; + } else if (b < a) { + return 1; + } else { + return 0; + } + } + + public static int compareTo(long a, long b) { + if (a < b) { + return -1; + } else if (b < a) { + return 1; + } else { + return 0; + } + } + + public static int compareTo(double a, double b) { + if (a < b) { + return -1; + } else if (b < a) { + return 1; + } else { + return 0; + } + } + + public static int compareTo(String a, String b) { + return a.compareTo(b); + } + + public static int compareTo(byte[] a, byte[] b) { + int sizeCompare = compareTo(a.length, b.length); + if (sizeCompare != 0) { + return sizeCompare; + } + for (int i = 0; i < a.length; i++) { + int byteCompare = compareTo(a, b); + if (byteCompare != 0) { + return byteCompare; + } + } + return 0; + } + + public static int compareTo(Object a, Object b) { + if (a instanceof Vector) { + return compareTo((Vector)a, (Vector)b); + } if (a instanceof Hashtable) { + return compareTo((Hashtable)a, (Hashtable)b); + } else { + return ((TBase)a).compareTo(b); + } + } + + public static int compareTo(Vector a, Vector b) { + int lastComparison = compareTo(a.size(), b.size()); + if (lastComparison != 0) { + return lastComparison; + } + for (int i = 0; i < a.size(); i++) { + Object oA = a.elementAt(i); + Object oB = b.elementAt(i); + lastComparison = compareTo(oA, oB); + if (lastComparison != 0) { + return lastComparison; + } + + } + return 0; + } + + public static int compareTo(Hashtable a, Hashtable b) { + int lastComparison = compareTo(a.size(), b.size()); + if (lastComparison != 0) { + return lastComparison; + } + Enumeration enumA = a.keys(); + Enumeration enumB = b.keys(); + while (lastComparison == 0 && enumA.hasMoreElements()) { + Object keyA = enumA.nextElement(); + Object keyB = enumB.nextElement(); + lastComparison = compareTo(keyA, keyB); + if (lastComparison == 0) { + lastComparison = compareTo(a.get(keyA), b.get(keyB)); + } + } + return lastComparison; + } + + public static int compareTo(TEnum a, TEnum b) { + return compareTo(a.getValue(), b.getValue()); + } + + /* + public static int compareTo(List a, List b) { + int lastComparison = compareTo(a.size(), b.size()); + if (lastComparison != 0) { + return lastComparison; + } + for (int i = 0; i < a.size(); i++) { + Object oA = a.get(i); + Object oB = b.get(i); + if (oA instanceof List) { + lastComparison = compareTo((List) oA, (List) oB); + } else { + lastComparison = compareTo((Comparable) oA, (Comparable) oB); + } + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + */ + + public static void toString(byte[] bytes, StringBuffer sb) { + toString(bytes, 0, bytes.length, sb); + } + + public static void toString(byte[] buf, int arrayOffset, int origLimit, StringBuffer sb) { + int limit = (origLimit - arrayOffset > 128) ? arrayOffset + 128 : origLimit; + + for (int i = arrayOffset; i < limit; i++) { + if (i > arrayOffset) { + sb.append(" "); + } + sb.append(paddedByteString(buf[i])); + } + if (origLimit != limit) { + sb.append("..."); + } + } + + public static String paddedByteString(byte b) { + int extended = (b | 0x100) & 0x1ff; + return Integer.toHexString(extended).toUpperCase().substring(1); + } + +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java new file mode 100644 index 000000000..077c2e6f7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TByteArrayOutputStream.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayOutputStream; + +/** + * Class that allows access to the underlying buf without doing deep + * copies on it. + * + */ +public class TByteArrayOutputStream extends ByteArrayOutputStream { + public TByteArrayOutputStream(int size) { + super(size); + } + + public byte[] get() { + return buf; + } + + public int len() { + return count; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java new file mode 100644 index 000000000..8eceae717 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TDeserializer.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; + +/** + * Generic utility for easily deserializing objects from a byte array or Java + * String. + * + */ +public class TDeserializer { + private final TProtocolFactory protocolFactory_; + + /** + * Create a new TDeserializer that uses the TBinaryProtocol by default. + */ + public TDeserializer() { + this(new TBinaryProtocol.Factory()); + } + + /** + * Create a new TDeserializer. It will use the TProtocol specified by the + * factory that is passed in. + * + * @param protocolFactory Factory to create a protocol + */ + public TDeserializer(TProtocolFactory protocolFactory) { + protocolFactory_ = protocolFactory; + } + + /** + * Deserialize the Thrift object from a byte array. + * + * @param base The object to read into + * @param bytes The array to read from + */ + public void deserialize(TBase base, byte[] bytes) throws TException { + base.read( + protocolFactory_.getProtocol( + new TIOStreamTransport( + new ByteArrayInputStream(bytes)))); + } + + /** + * Deserialize the Thrift object from a Java string, using a specified + * character set for decoding. + * + * @param base The object to read into + * @param data The string to read from + * @param charset Valid JVM charset + */ + public void deserialize(TBase base, String data, String charset) throws TException { + try { + deserialize(base, data.getBytes(charset)); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset); + } + } + + /** + * Deerialize the Thrift object from a Java string, using the default JVM + * charset encoding. + * + * @param base The object to read into + * @param data The string to read from + * @return Serialized object as a String + */ + public void toString(TBase base, String data) throws TException { + deserialize(base, data.getBytes()); + } +} + diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java new file mode 100644 index 000000000..325fdece7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TEnum.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +public interface TEnum { + public int getValue(); +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java new file mode 100644 index 000000000..7f375b813 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TException.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Generic exception class for Thrift. + * + */ +public class TException extends Exception { + + private static final long serialVersionUID = 1L; + + public TException() { + super(); + } + + public TException(String message) { + super(message); + } + + public TException(Throwable cause) { + super(cause.getMessage()); + } + + public TException(String message, Throwable cause) { + super(message); + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java new file mode 100644 index 000000000..104a391d8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TFieldRequirementType.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +/** + * Requirement type constants. + * + */ +public final class TFieldRequirementType { + public static final byte REQUIRED = 1; + public static final byte OPTIONAL = 2; + public static final byte DEFAULT = 3; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java new file mode 100644 index 000000000..d79522c3e --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessor.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TProtocol; + +/** + * A processor is a generic object which operates upon an input stream and + * writes to some output stream. + * + */ +public interface TProcessor { + public boolean process(TProtocol in, TProtocol out) + throws TException; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java new file mode 100644 index 000000000..bcd8a38fd --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TProcessorFactory.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.transport.TTransport; + +/** + * The default processor factory just returns a singleton + * instance. + */ +public class TProcessorFactory { + + private final TProcessor processor_; + + public TProcessorFactory(TProcessor processor) { + processor_ = processor; + } + + public TProcessor getProcessor(TTransport trans) { + return processor_; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java new file mode 100644 index 000000000..4e1ce6129 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TSerializer.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; + +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TIOStreamTransport; + +/** + * Generic utility for easily serializing objects into a byte array or Java + * String. + * + */ +public class TSerializer { + + /** + * This is the byte array that data is actually serialized into + */ + private final ByteArrayOutputStream baos_ = new ByteArrayOutputStream(); + + /** + * This transport wraps that byte array + */ + private final TIOStreamTransport transport_ = new TIOStreamTransport(baos_); + + /** + * Internal protocol used for serializing objects. + */ + private TProtocol protocol_; + + /** + * Create a new TSerializer that uses the TBinaryProtocol by default. + */ + public TSerializer() { + this(new TBinaryProtocol.Factory()); + } + + /** + * Create a new TSerializer. It will use the TProtocol specified by the + * factory that is passed in. + * + * @param protocolFactory Factory to create a protocol + */ + public TSerializer(TProtocolFactory protocolFactory) { + protocol_ = protocolFactory.getProtocol(transport_); + } + + /** + * Serialize the Thrift object into a byte array. The process is simple, + * just clear the byte array output, write the object into it, and grab the + * raw bytes. + * + * @param base The object to serialize + * @return Serialized object in byte[] format + */ + public byte[] serialize(TBase base) throws TException { + baos_.reset(); + base.write(protocol_); + return baos_.toByteArray(); + } + + /** + * Serialize the Thrift object into a Java string, using a specified + * character set for encoding. + * + * @param base The object to serialize + * @param charset Valid JVM charset + * @return Serialized object as a String + */ + public String toString(TBase base, String charset) throws TException { + try { + return new String(serialize(base), charset); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT ENCODING: " + charset); + } + } + + /** + * Serialize the Thrift object into a Java string, using the default JVM + * charset encoding. + * + * @param base The object to serialize + * @return Serialized object as a String + */ + public String toString(TBase base) throws TException { + return new String(serialize(base)); + } +} + diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java new file mode 100644 index 000000000..ee07b7821 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/TServiceClient.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift; + +import org.apache.thrift.protocol.TProtocol; + +/** + * A TServiceClient is used to communicate with a TService implementation + * across protocols and transports. + */ +public interface TServiceClient { + /** + * Get the TProtocol being used as the input (read) protocol. + * @return + */ + public TProtocol getInputProtocol(); + /** + * Get the TProtocol being used as the output (write) protocol. + * @return + */ + public TProtocol getOutputProtocol(); +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java new file mode 100644 index 000000000..bce02c724 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldMetaData.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.TBase; + +import java.util.Hashtable; + + +/** + * This class is used to store meta data about thrift fields. Every field in a + * a struct should have a corresponding instance of this class describing it. + * + */ +public class FieldMetaData { + public final String fieldName; + public final byte requirementType; + public final FieldValueMetaData valueMetaData; + private static Hashtable structMap; + + static { + structMap = new Hashtable(); + } + + public FieldMetaData(String name, byte req, FieldValueMetaData vMetaData){ + this.fieldName = name; + this.requirementType = req; + this.valueMetaData = vMetaData; + } + + public static synchronized void addStructMetaDataMap(Class sClass, Hashtable map){ + structMap.put(sClass, map); + } + + /** + * Returns a map with metadata (i.e. instances of FieldMetaData) that + * describe the fields of the given class. + * + * @param sClass The TBase class for which the metadata map is requested + */ + public static synchronized Hashtable getStructMetaDataMap(Class sClass){ + if (!structMap.containsKey(sClass)){ // Load class if it hasn't been loaded + try{ + sClass.newInstance(); + } catch (InstantiationException e){ + throw new RuntimeException("InstantiationException for TBase class: " + sClass.getName() + ", message: " + e.getMessage()); + } catch (IllegalAccessException e){ + throw new RuntimeException("IllegalAccessException for TBase class: " + sClass.getName() + ", message: " + e.getMessage()); + } + } + return (Hashtable) structMap.get(sClass); + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java new file mode 100644 index 000000000..4c017b6bf --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/FieldValueMetaData.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.protocol.TType; + + +/** + * FieldValueMetaData and collection of subclasses to store metadata about + * the value(s) of a field + */ +public class FieldValueMetaData { + public final byte type; + + public FieldValueMetaData(byte type){ + this.type = type; + } + + public boolean isStruct() { + return type == TType.STRUCT; + } + + public boolean isContainer() { + return type == TType.LIST || type == TType.MAP || type == TType.SET; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java new file mode 100644 index 000000000..5cd719423 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/ListMetaData.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.meta_data.FieldValueMetaData; + +public class ListMetaData extends FieldValueMetaData { + public final FieldValueMetaData elemMetaData; + + public ListMetaData(byte type, FieldValueMetaData eMetaData){ + super(type); + this.elemMetaData = eMetaData; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java new file mode 100644 index 000000000..0d1d6f659 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/MapMetaData.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.meta_data.FieldValueMetaData; + +public class MapMetaData extends FieldValueMetaData { + public final FieldValueMetaData keyMetaData; + public final FieldValueMetaData valueMetaData; + + public MapMetaData(byte type, FieldValueMetaData kMetaData, FieldValueMetaData vMetaData){ + super(type); + this.keyMetaData = kMetaData; + this.valueMetaData = vMetaData; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java new file mode 100644 index 000000000..3ec6868a1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/SetMetaData.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +public class SetMetaData extends FieldValueMetaData { + public final FieldValueMetaData elemMetaData; + + public SetMetaData(byte type, FieldValueMetaData eMetaData){ + super(type); + this.elemMetaData = eMetaData; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java new file mode 100644 index 000000000..e30d90814 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/meta_data/StructMetaData.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.meta_data; + +import org.apache.thrift.TBase; +import org.apache.thrift.meta_data.FieldValueMetaData; + + +public class StructMetaData extends FieldValueMetaData { + public final Class structClass; + + public StructMetaData(byte type, Class sClass){ + super(type); + this.structClass = sClass; + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java new file mode 100644 index 000000000..abfc965b7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBase64Utils.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Class for encoding and decoding Base64 data. + * + * This class is kept at package level because the interface does no input + * validation and is therefore too low-level for generalized reuse. + * + * Note also that the encoding does not pad with equal signs , as discussed in + * section 2.2 of the RFC (http://www.faqs.org/rfcs/rfc3548.html). Furthermore, + * bad data encountered when decoding is neither rejected or ignored but simply + * results in bad decoded data -- this is not in compliance with the RFC but is + * done in the interest of performance. + * + */ +class TBase64Utils { + + private static final String ENCODE_TABLE = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + /** + * Encode len bytes of data in src at offset srcOff, storing the result into + * dst at offset dstOff. len must be 1, 2, or 3. dst must have at least len+1 + * bytes of space at dstOff. src and dst should not be the same object. This + * method does no validation of the input values in the interest of + * performance. + * + * @param src the source of bytes to encode + * @param srcOff the offset into the source to read the unencoded bytes + * @param len the number of bytes to encode (must be 1, 2, or 3). + * @param dst the destination for the encoding + * @param dstOff the offset into the destination to place the encoded bytes + */ + static final void encode(byte[] src, int srcOff, int len, byte[] dst, + int dstOff) { + dst[dstOff] = (byte)ENCODE_TABLE.charAt((src[srcOff] >> 2) & 0x3F); + if (len == 3) { + dst[dstOff + 1] = + (byte)ENCODE_TABLE.charAt( + ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F)); + dst[dstOff + 2] = + (byte)ENCODE_TABLE.charAt( + ((src[srcOff+1] << 2) & 0x3C) | ((src[srcOff+2] >> 6) & 0x03)); + dst[dstOff + 3] = + (byte)ENCODE_TABLE.charAt(src[srcOff+2] & 0x3F); + } + else if (len == 2) { + dst[dstOff+1] = + (byte)ENCODE_TABLE.charAt( + ((src[srcOff] << 4) & 0x30) | ((src[srcOff+1] >> 4) & 0x0F)); + dst[dstOff + 2] = + (byte)ENCODE_TABLE.charAt((src[srcOff+1] << 2) & 0x3C); + } + else { // len == 1) { + dst[dstOff + 1] = + (byte)ENCODE_TABLE.charAt((src[srcOff] << 4) & 0x30); + } + } + + private static final byte[] DECODE_TABLE = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + }; + + /** + * Decode len bytes of data in src at offset srcOff, storing the result into + * dst at offset dstOff. len must be 2, 3, or 4. dst must have at least len-1 + * bytes of space at dstOff. src and dst may be the same object as long as + * dstoff <= srcOff. This method does no validation of the input values in + * the interest of performance. + * + * @param src the source of bytes to decode + * @param srcOff the offset into the source to read the encoded bytes + * @param len the number of bytes to decode (must be 2, 3, or 4) + * @param dst the destination for the decoding + * @param dstOff the offset into the destination to place the decoded bytes + */ + static final void decode(byte[] src, int srcOff, int len, byte[] dst, + int dstOff) { + dst[dstOff] = (byte) + ((DECODE_TABLE[src[srcOff] & 0x0FF] << 2) | + (DECODE_TABLE[src[srcOff+1] & 0x0FF] >> 4)); + if (len > 2) { + dst[dstOff+1] = (byte) + (((DECODE_TABLE[src[srcOff+1] & 0x0FF] << 4) & 0xF0) | + (DECODE_TABLE[src[srcOff+2] & 0x0FF] >> 2)); + if (len > 3) { + dst[dstOff+2] = (byte) + (((DECODE_TABLE[src[srcOff+2] & 0x0FF] << 6) & 0xC0) | + DECODE_TABLE[src[srcOff+3] & 0x0FF]); + } + } + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java new file mode 100644 index 000000000..f6fe76543 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TBinaryProtocol.java @@ -0,0 +1,329 @@ + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.io.UnsupportedEncodingException; + +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * Binary protocol implementation for thrift. + * + */ +public class TBinaryProtocol extends TProtocol { + + protected static final int VERSION_MASK = 0xffff0000; + protected static final int VERSION_1 = 0x80010000; + + protected boolean strictRead_ = false; + protected boolean strictWrite_ = true; + + /** + * Factory + */ + public static class Factory implements TProtocolFactory { + protected boolean strictRead_ = false; + protected boolean strictWrite_ = true; + + public Factory() { + this(false, true); + } + + public Factory(boolean strictRead, boolean strictWrite) { + strictRead_ = strictRead; + strictWrite_ = strictWrite; + } + + public TProtocol getProtocol(TTransport trans) { + return new TBinaryProtocol(trans, strictRead_, strictWrite_); + } + } + + /** + * Constructor + */ + public TBinaryProtocol(TTransport trans) { + this(trans, false, true); + } + + public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) { + super(trans); + strictRead_ = strictRead; + strictWrite_ = strictWrite; + } + + public void writeMessageBegin(TMessage message) throws TException { + if (strictWrite_) { + int version = VERSION_1 | message.type; + writeI32(version); + writeString(message.name); + writeI32(message.seqid); + } else { + writeString(message.name); + writeByte(message.type); + writeI32(message.seqid); + } + } + + public void writeMessageEnd() {} + + public void writeStructBegin(TStruct struct) {} + + public void writeStructEnd() {} + + public void writeFieldBegin(TField field) throws TException { + writeByte(field.type); + writeI16(field.id); + } + + public void writeFieldEnd() {} + + public void writeFieldStop() throws TException { + writeByte(TType.STOP); + } + + public void writeMapBegin(TMap map) throws TException { + writeByte(map.keyType); + writeByte(map.valueType); + writeI32(map.size); + } + + public void writeMapEnd() {} + + public void writeListBegin(TList list) throws TException { + writeByte(list.elemType); + writeI32(list.size); + } + + public void writeListEnd() {} + + public void writeSetBegin(TSet set) throws TException { + writeByte(set.elemType); + writeI32(set.size); + } + + public void writeSetEnd() {} + + public void writeBool(boolean b) throws TException { + writeByte(b ? (byte)1 : (byte)0); + } + + private byte [] bout = new byte[1]; + public void writeByte(byte b) throws TException { + bout[0] = b; + trans_.write(bout, 0, 1); + } + + private byte[] i16out = new byte[2]; + public void writeI16(short i16) throws TException { + i16out[0] = (byte)(0xff & (i16 >> 8)); + i16out[1] = (byte)(0xff & (i16)); + trans_.write(i16out, 0, 2); + } + + private byte[] i32out = new byte[4]; + public void writeI32(int i32) throws TException { + i32out[0] = (byte)(0xff & (i32 >> 24)); + i32out[1] = (byte)(0xff & (i32 >> 16)); + i32out[2] = (byte)(0xff & (i32 >> 8)); + i32out[3] = (byte)(0xff & (i32)); + trans_.write(i32out, 0, 4); + } + + private byte[] i64out = new byte[8]; + public void writeI64(long i64) throws TException { + i64out[0] = (byte)(0xff & (i64 >> 56)); + i64out[1] = (byte)(0xff & (i64 >> 48)); + i64out[2] = (byte)(0xff & (i64 >> 40)); + i64out[3] = (byte)(0xff & (i64 >> 32)); + i64out[4] = (byte)(0xff & (i64 >> 24)); + i64out[5] = (byte)(0xff & (i64 >> 16)); + i64out[6] = (byte)(0xff & (i64 >> 8)); + i64out[7] = (byte)(0xff & (i64)); + trans_.write(i64out, 0, 8); + } + + public void writeDouble(double dub) throws TException { + writeI64(Double.doubleToLongBits(dub)); + } + + public void writeString(String str) throws TException { + try { + byte[] dat = str.getBytes("UTF-8"); + writeI32(dat.length); + trans_.write(dat, 0, dat.length); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + } + + public void writeBinary(byte[] bin) throws TException { + writeI32(bin.length); + trans_.write(bin, 0, bin.length); + } + + /** + * Reading methods. + */ + + public TMessage readMessageBegin() throws TException { + TMessage message = new TMessage(); + + int size = readI32(); + if (size < 0) { + int version = size & VERSION_MASK; + if (version != VERSION_1) { + throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin"); + } + message.type = (byte)(size & 0x000000ff); + message.name = readString(); + message.seqid = readI32(); + } else { + if (strictRead_) { + throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?"); + } + message.name = readStringBody(size); + message.type = readByte(); + message.seqid = readI32(); + } + return message; + } + + public void readMessageEnd() {} + + public TStruct readStructBegin() { + return new TStruct(); + } + + public void readStructEnd() {} + + public TField readFieldBegin() throws TException { + TField field = new TField(); + field.type = readByte(); + if (field.type != TType.STOP) { + field.id = readI16(); + } + return field; + } + + public void readFieldEnd() {} + + public TMap readMapBegin() throws TException { + TMap map = new TMap(); + map.keyType = readByte(); + map.valueType = readByte(); + map.size = readI32(); + return map; + } + + public void readMapEnd() {} + + public TList readListBegin() throws TException { + TList list = new TList(); + list.elemType = readByte(); + list.size = readI32(); + return list; + } + + public void readListEnd() {} + + public TSet readSetBegin() throws TException { + TSet set = new TSet(); + set.elemType = readByte(); + set.size = readI32(); + return set; + } + + public void readSetEnd() {} + + public boolean readBool() throws TException { + return (readByte() == 1); + } + + private byte[] bin = new byte[1]; + public byte readByte() throws TException { + readAll(bin, 0, 1); + return bin[0]; + } + + private byte[] i16rd = new byte[2]; + public short readI16() throws TException { + readAll(i16rd, 0, 2); + return + (short) + (((i16rd[0] & 0xff) << 8) | + ((i16rd[1] & 0xff))); + } + + private byte[] i32rd = new byte[4]; + public int readI32() throws TException { + readAll(i32rd, 0, 4); + return + ((i32rd[0] & 0xff) << 24) | + ((i32rd[1] & 0xff) << 16) | + ((i32rd[2] & 0xff) << 8) | + ((i32rd[3] & 0xff)); + } + + private byte[] i64rd = new byte[8]; + public long readI64() throws TException { + readAll(i64rd, 0, 8); + return + ((long)(i64rd[0] & 0xff) << 56) | + ((long)(i64rd[1] & 0xff) << 48) | + ((long)(i64rd[2] & 0xff) << 40) | + ((long)(i64rd[3] & 0xff) << 32) | + ((long)(i64rd[4] & 0xff) << 24) | + ((long)(i64rd[5] & 0xff) << 16) | + ((long)(i64rd[6] & 0xff) << 8) | + ((long)(i64rd[7] & 0xff)); + } + + public double readDouble() throws TException { + return Double.longBitsToDouble(readI64()); + } + + public String readString() throws TException { + int size = readI32(); + return readStringBody(size); + } + + public String readStringBody(int size) throws TException { + try { + byte[] buf = new byte[size]; + trans_.readAll(buf, 0, size); + return new String(buf, "UTF-8"); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + } + + public byte[] readBinary() throws TException { + int size = readI32(); + byte[] buf = new byte[size]; + trans_.readAll(buf, 0, size); + return buf; + } + + private int readAll(byte[] buf, int off, int len) throws TException { + return trans_.readAll(buf, off, len); + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java new file mode 100644 index 000000000..061140ff8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TField.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates field metadata. + * + */ +public class TField { + public TField() {} + + public TField(String n, byte t, short i) { + name = n; + type = t; + id = i; + } + + public String name = ""; + public byte type = TType.STOP; + public short id = 0; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java new file mode 100644 index 000000000..d3916863b --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TJSONProtocol.java @@ -0,0 +1,973 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Stack; + +import org.apache.thrift.TByteArrayOutputStream; +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * JSON protocol implementation for thrift. + * This is a full-featured protocol supporting write and read. + * Please see the C++ class header for a detailed description of the + * protocol's wire format. + */ +public class TJSONProtocol extends TProtocol { + + /** + * Factory for JSON protocol objects + */ + public static class Factory implements TProtocolFactory { + + public TProtocol getProtocol(TTransport trans) { + return new TJSONProtocol(trans); + } + + } + + private static final byte[] COMMA = new byte[] { ',' }; + private static final byte[] COLON = new byte[] { ':' }; + private static final byte[] LBRACE = new byte[] { '{' }; + private static final byte[] RBRACE = new byte[] { '}' }; + private static final byte[] LBRACKET = new byte[] { '[' }; + private static final byte[] RBRACKET = new byte[] { ']' }; + private static final byte[] QUOTE = new byte[] { '"' }; + private static final byte[] BACKSLASH = new byte[] { '\\' }; + private static final byte[] ZERO = new byte[] { '0' }; + + private static final byte[] ESCSEQ = new byte[] { '\\', 'u', '0', '0' }; + + private static final long VERSION = 1; + + private static final byte[] JSON_CHAR_TABLE = { + /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 1, '"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + }; + + private static final String ESCAPE_CHARS = "\"\\/bfnrt"; + + private static final byte[] ESCAPE_CHAR_VALS = { + '"', '\\', '/', '\b', '\f', '\n', '\r', '\t', + }; + + private static final int DEF_STRING_SIZE = 16; + + private static final byte[] NAME_BOOL = new byte[] { 't', 'f' }; + private static final byte[] NAME_BYTE = new byte[] { 'i', '8' }; + private static final byte[] NAME_I16 = new byte[] { 'i', '1', '6' }; + private static final byte[] NAME_I32 = new byte[] { 'i', '3', '2' }; + private static final byte[] NAME_I64 = new byte[] { 'i', '6', '4' }; + private static final byte[] NAME_DOUBLE = new byte[] { 'd', 'b', 'l' }; + private static final byte[] NAME_STRUCT = new byte[] { 'r', 'e', 'c' }; + private static final byte[] NAME_STRING = new byte[] { 's', 't', 'r' }; + private static final byte[] NAME_MAP = new byte[] { 'm', 'a', 'p' }; + private static final byte[] NAME_LIST = new byte[] { 'l', 's', 't' }; + private static final byte[] NAME_SET = new byte[] { 's', 'e', 't' }; + + private static final TStruct ANONYMOUS_STRUCT = new TStruct(); + + private static final byte[] getTypeNameForTypeID(byte typeID) + throws TException { + switch (typeID) { + case TType.BOOL: + return NAME_BOOL; + case TType.BYTE: + return NAME_BYTE; + case TType.I16: + return NAME_I16; + case TType.I32: + return NAME_I32; + case TType.I64: + return NAME_I64; + case TType.DOUBLE: + return NAME_DOUBLE; + case TType.STRING: + return NAME_STRING; + case TType.STRUCT: + return NAME_STRUCT; + case TType.MAP: + return NAME_MAP; + case TType.SET: + return NAME_SET; + case TType.LIST: + return NAME_LIST; + default: + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Unrecognized type"); + } + } + + private static final byte getTypeIDForTypeName(byte[] name) + throws TException { + byte result = TType.STOP; + if (name.length > 1) { + switch (name[0]) { + case 'd': + result = TType.DOUBLE; + break; + case 'i': + switch (name[1]) { + case '8': + result = TType.BYTE; + break; + case '1': + result = TType.I16; + break; + case '3': + result = TType.I32; + break; + case '6': + result = TType.I64; + break; + } + break; + case 'l': + result = TType.LIST; + break; + case 'm': + result = TType.MAP; + break; + case 'r': + result = TType.STRUCT; + break; + case 's': + if (name[1] == 't') { + result = TType.STRING; + } + else if (name[1] == 'e') { + result = TType.SET; + } + break; + case 't': + result = TType.BOOL; + break; + } + } + if (result == TType.STOP) { + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "Unrecognized type"); + } + return result; + } + + // Base class for tracking JSON contexts that may require inserting/reading + // additional JSON syntax characters + // This base context does nothing. + protected class JSONBaseContext { + /** + * @throws TException + */ + protected void write() throws TException {} + + /** + * @throws TException + */ + protected void read() throws TException {} + + protected boolean escapeNum() { + return false; + } + } + + // Context for JSON lists. Will insert/read commas before each item except + // for the first one + protected class JSONListContext extends JSONBaseContext { + private boolean first_ = true; + + protected void write() throws TException { + if (first_) { + first_ = false; + } else { + trans_.write(COMMA); + } + } + + protected void read() throws TException { + if (first_) { + first_ = false; + } else { + readJSONSyntaxChar(COMMA); + } + } + } + + // Context for JSON records. Will insert/read colons before the value portion + // of each record pair, and commas before each key except the first. In + // addition, will indicate that numbers in the key position need to be + // escaped in quotes (since JSON keys must be strings). + protected class JSONPairContext extends JSONBaseContext { + private boolean first_ = true; + private boolean colon_ = true; + + protected void write() throws TException { + if (first_) { + first_ = false; + colon_ = true; + } else { + trans_.write(colon_ ? COLON : COMMA); + colon_ = !colon_; + } + } + + protected void read() throws TException { + if (first_) { + first_ = false; + colon_ = true; + } else { + readJSONSyntaxChar(colon_ ? COLON : COMMA); + colon_ = !colon_; + } + } + + protected boolean escapeNum() { + return colon_; + } + } + + // Holds up to one byte from the transport + protected class LookaheadReader { + + private boolean hasData_; + private byte[] data_ = new byte[1]; + + // Return and consume the next byte to be read, either taking it from the + // data buffer if present or getting it from the transport otherwise. + protected byte read() throws TException { + if (hasData_) { + hasData_ = false; + } + else { + trans_.readAll(data_, 0, 1); + } + return data_[0]; + } + + // Return the next byte to be read without consuming, filling the data + // buffer if it has not been filled already. + protected byte peek() throws TException { + if (!hasData_) { + trans_.readAll(data_, 0, 1); + } + hasData_ = true; + return data_[0]; + } + } + + // Stack of nested contexts of type JSONBaseContext that we may be in + private Stack contextStack_ = new Stack(); + + // Current context that we are in + private JSONBaseContext context_ = new JSONBaseContext(); + + // Reader that manages a 1-byte buffer + private LookaheadReader reader_ = new LookaheadReader(); + + // Push a new JSON context onto the stack. + private void pushContext(JSONBaseContext c) { + contextStack_.push(context_); + context_ = c; + } + + // Pop the last JSON context off the stack + private void popContext() { + context_ = (JSONBaseContext)contextStack_.pop(); + } + + /** + * Constructor + */ + public TJSONProtocol(TTransport trans) { + super(trans); + } + + public void reset() { + contextStack_.clear(); + context_ = new JSONBaseContext(); + reader_ = new LookaheadReader(); + } + + // Temporary buffer used by several methods + private byte[] tmpbuf_ = new byte[4]; + + // Read a byte that must match b[0]; otherwise an exception is thrown. + // Marked protected to avoid synthetic accessor in JSONListContext.read + // and JSONPairContext.read + protected void readJSONSyntaxChar(byte[] b) throws TException { + byte ch = reader_.read(); + if (ch != b[0]) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Unexpected character:" + (char)ch); + } + } + + // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its + // corresponding hex value + private static final byte hexVal(byte ch) throws TException { + if ((ch >= '0') && (ch <= '9')) { + return (byte)((char)ch - '0'); + } + else if ((ch >= 'a') && (ch <= 'f')) { + return (byte)((char)ch - 'a' + 10); + } + else { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected hex character"); + } + } + + // Convert a byte containing a hex value to its corresponding hex character + private static final byte hexChar(byte val) { + val &= 0x0F; + if (val < 10) { + return (byte)((char)val + '0'); + } + else { + return (byte)((char)(val - 10) + 'a'); + } + } + + private static boolean isHighSurrogate(char c) { + return c >= '\uD800' && c <= '\uDBFF'; + } + + private static boolean isLowSurrogate(char c) { + return c >= '\uDC00' && c <= '\uDFFF'; + } + + private static byte[] toUTF8(int codepoint) { + final int[] FIRST_BYTE_MASK = { 0, 0xc0, 0xe0, 0xf0 }; + int length = 0; + if (codepoint <= 0x7f) length = 1; + else if (codepoint <= 0x7ff) length = 2; + else if (codepoint <= 0xffff) length = 3; + else if (codepoint <= 0x1fffff) length = 4; + else throw new RuntimeException("Code point over U+1FFFFF is not supported"); + + byte[] bytes = new byte[length]; + switch (length) { + case 4: + bytes[3] = (byte)((codepoint & 0x3f) | 0x80); + codepoint >>= 6; + case 3: + bytes[2] = (byte)((codepoint & 0x3f) | 0x80); + codepoint >>= 6; + case 2: + bytes[1] = (byte)((codepoint & 0x3f) | 0x80); + codepoint >>= 6; + case 1: + bytes[0] = (byte)(codepoint | FIRST_BYTE_MASK[length - 1]); + } + + return bytes; + } + + private static byte[] toUTF8(int high, int low) { + int codepoint = (1 << 16) + ((high & 0x3ff) << 10); + codepoint += low & 0x3ff; + return toUTF8(codepoint); + } + + // Write the bytes in array buf as a JSON characters, escaping as needed + private void writeJSONString(byte[] b) throws TException { + context_.write(); + trans_.write(QUOTE); + int len = b.length; + for (int i = 0; i < len; i++) { + if ((b[i] & 0x00FF) >= 0x30) { + if (b[i] == BACKSLASH[0]) { + trans_.write(BACKSLASH); + trans_.write(BACKSLASH); + } + else { + trans_.write(b, i, 1); + } + } + else { + tmpbuf_[0] = JSON_CHAR_TABLE[b[i]]; + if (tmpbuf_[0] == 1) { + trans_.write(b, i, 1); + } + else if (tmpbuf_[0] > 1) { + trans_.write(BACKSLASH); + trans_.write(tmpbuf_, 0, 1); + } + else { + trans_.write(ESCSEQ); + tmpbuf_[0] = hexChar((byte)(b[i] >> 4)); + tmpbuf_[1] = hexChar(b[i]); + trans_.write(tmpbuf_, 0, 2); + } + } + } + trans_.write(QUOTE); + } + + // Write out number as a JSON value. If the context dictates so, it will be + // wrapped in quotes to output as a JSON string. + private void writeJSONInteger(long num) throws TException { + context_.write(); + String str = Long.toString(num); + boolean escapeNum = context_.escapeNum(); + if (escapeNum) { + trans_.write(QUOTE); + } + try { + byte[] buf = str.getBytes("UTF-8"); + trans_.write(buf); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + if (escapeNum) { + trans_.write(QUOTE); + } + } + + // Write out a double as a JSON value. If it is NaN or infinity or if the + // context dictates escaping, write out as JSON string. + private void writeJSONDouble(double num) throws TException { + context_.write(); + String str = Double.toString(num); + boolean special = false; + switch (str.charAt(0)) { + case 'N': // NaN + case 'I': // Infinity + special = true; + break; + case '-': + if (str.charAt(1) == 'I') { // -Infinity + special = true; + } + break; + } + + boolean escapeNum = special || context_.escapeNum(); + if (escapeNum) { + trans_.write(QUOTE); + } + try { + byte[] b = str.getBytes("UTF-8"); + trans_.write(b, 0, b.length); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + if (escapeNum) { + trans_.write(QUOTE); + } + } + + // Write out contents of byte array b as a JSON string with base-64 encoded + // data + private void writeJSONBase64(byte[] b, int offset, int length) throws TException { + context_.write(); + trans_.write(QUOTE); + int len = length; + int off = offset; + while (len >= 3) { + // Encode 3 bytes at a time + TBase64Utils.encode(b, off, 3, tmpbuf_, 0); + trans_.write(tmpbuf_, 0, 4); + off += 3; + len -= 3; + } + if (len > 0) { + // Encode remainder + TBase64Utils.encode(b, off, len, tmpbuf_, 0); + trans_.write(tmpbuf_, 0, len + 1); + } + trans_.write(QUOTE); + } + + private void writeJSONObjectStart() throws TException { + context_.write(); + trans_.write(LBRACE); + pushContext(new JSONPairContext()); + } + + private void writeJSONObjectEnd() throws TException { + popContext(); + trans_.write(RBRACE); + } + + private void writeJSONArrayStart() throws TException { + context_.write(); + trans_.write(LBRACKET); + pushContext(new JSONListContext()); + } + + private void writeJSONArrayEnd() throws TException { + popContext(); + trans_.write(RBRACKET); + } + + public void writeMessageBegin(TMessage message) throws TException { + writeJSONArrayStart(); + writeJSONInteger(VERSION); + try { + byte[] b = message.name.getBytes("UTF-8"); + writeJSONString(b); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + writeJSONInteger(message.type); + writeJSONInteger(message.seqid); + } + + public void writeMessageEnd() throws TException { + writeJSONArrayEnd(); + } + + public void writeStructBegin(TStruct struct) throws TException { + writeJSONObjectStart(); + } + + public void writeStructEnd() throws TException { + writeJSONObjectEnd(); + } + + public void writeFieldBegin(TField field) throws TException { + writeJSONInteger(field.id); + writeJSONObjectStart(); + writeJSONString(getTypeNameForTypeID(field.type)); + } + + public void writeFieldEnd() throws TException { + writeJSONObjectEnd(); + } + + public void writeFieldStop() {} + + public void writeMapBegin(TMap map) throws TException { + writeJSONArrayStart(); + writeJSONString(getTypeNameForTypeID(map.keyType)); + writeJSONString(getTypeNameForTypeID(map.valueType)); + writeJSONInteger(map.size); + writeJSONObjectStart(); + } + + public void writeMapEnd() throws TException { + writeJSONObjectEnd(); + writeJSONArrayEnd(); + } + + public void writeListBegin(TList list) throws TException { + writeJSONArrayStart(); + writeJSONString(getTypeNameForTypeID(list.elemType)); + writeJSONInteger(list.size); + } + + public void writeListEnd() throws TException { + writeJSONArrayEnd(); + } + + public void writeSetBegin(TSet set) throws TException { + writeJSONArrayStart(); + writeJSONString(getTypeNameForTypeID(set.elemType)); + writeJSONInteger(set.size); + } + + public void writeSetEnd() throws TException { + writeJSONArrayEnd(); + } + + public void writeBool(boolean b) throws TException { + writeJSONInteger(b ? (long)1 : (long)0); + } + + public void writeByte(byte b) throws TException { + writeJSONInteger(b); + } + + public void writeI16(short i16) throws TException { + writeJSONInteger(i16); + } + + public void writeI32(int i32) throws TException { + writeJSONInteger(i32); + } + + public void writeI64(long i64) throws TException { + writeJSONInteger(i64); + } + + public void writeDouble(double dub) throws TException { + writeJSONDouble(dub); + } + + public void writeString(String str) throws TException { + try { + byte[] b = str.getBytes("UTF-8"); + writeJSONString(b); + } catch (UnsupportedEncodingException uex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + } + + public void writeBinary(byte[] bin) throws TException { + writeJSONBase64(bin, 0, bin.length); + } + + /** + * Reading methods. + */ + + // Read in a JSON string, unescaping as appropriate.. Skip reading from the + // context if skipContext is true. + private TByteArrayOutputStream readJSONString(boolean skipContext) + throws TException { + TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE); + int highSurrogate = 0; + if (!skipContext) { + context_.read(); + } + readJSONSyntaxChar(QUOTE); + while (true) { + byte ch = reader_.read(); + if (ch == QUOTE[0]) { + break; + } + if (ch == ESCSEQ[0]) { + ch = reader_.read(); + if (ch == ESCSEQ[1]) { + trans_.readAll(tmpbuf_, 0, 4); + short cu = (short)( + ((short)hexVal(tmpbuf_[0]) << 12) + + ((short)hexVal(tmpbuf_[1]) << 8) + + ((short)hexVal(tmpbuf_[2]) << 4) + + (short)hexVal(tmpbuf_[3])); + try { + if (isHighSurrogate((char)cu)) { + if (highSurrogate != 0) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected low surrogate char"); + } + highSurrogate = cu; + } + else if (isLowSurrogate((char)cu)) { + if (highSurrogate == 0) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected high surrogate char"); + } + + arr.write(toUTF8(highSurrogate, cu)); + highSurrogate = 0; + } + else { + arr.write(toUTF8(cu)); + } + continue; + } + catch (UnsupportedEncodingException ex) { + throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED, + "JVM does not support UTF-8"); + } + catch (IOException ex) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Invalid unicode sequence"); + } + } + else { + int off = ESCAPE_CHARS.indexOf(ch); + if (off == -1) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected control char"); + } + ch = ESCAPE_CHAR_VALS[off]; + } + } + arr.write(ch); + } + + if (highSurrogate != 0) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Expected low surrogate char"); + } + return arr; + } + + // Return true if the given byte could be a valid part of a JSON number. + private boolean isJSONNumeric(byte b) { + switch (b) { + case '+': + case '-': + case '.': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'E': + case 'e': + return true; + } + return false; + } + + // Read in a sequence of characters that are all valid in JSON numbers. Does + // not do a complete regex check to validate that this is actually a number. + private String readJSONNumericChars() throws TException { + StringBuffer strbuf = new StringBuffer(); + while (true) { + byte ch = reader_.peek(); + if (!isJSONNumeric(ch)) { + break; + } + strbuf.append((char)reader_.read()); + } + return strbuf.toString(); + } + + // Read in a JSON number. If the context dictates, read in enclosing quotes. + private long readJSONInteger() throws TException { + context_.read(); + if (context_.escapeNum()) { + readJSONSyntaxChar(QUOTE); + } + String str = readJSONNumericChars(); + if (context_.escapeNum()) { + readJSONSyntaxChar(QUOTE); + } + try { + return Long.valueOf(str).longValue(); + } catch (NumberFormatException ex) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data"); + } + } + + // Read in a JSON double value. Throw if the value is not wrapped in quotes + // when expected or if wrapped in quotes when not expected. + private double readJSONDouble() throws TException { + context_.read(); + if (reader_.peek() == QUOTE[0]) { + TByteArrayOutputStream arr = readJSONString(true); + try { + double dub = Double.valueOf(arr.toString("UTF-8")).doubleValue(); + if (!context_.escapeNum() && !Double.isNaN(dub) && + !Double.isInfinite(dub)) { + // Throw exception -- we should not be in a string in this case + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Numeric data unexpectedly quoted"); + } + return dub; + } catch (UnsupportedEncodingException ex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + } + else { + if (context_.escapeNum()) { + // This will throw - we should have had a quote if escapeNum == true + readJSONSyntaxChar(QUOTE); + } + try { + return Double.valueOf(readJSONNumericChars()).doubleValue(); + } catch (NumberFormatException ex) { + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Bad data encounted in numeric data"); + } + } + } + + // Read in a JSON string containing base-64 encoded data and decode it. + private byte[] readJSONBase64() throws TException { + TByteArrayOutputStream arr = readJSONString(false); + byte[] b = arr.get(); + int len = arr.len(); + int off = 0; + int size = 0; + // Ignore padding + int bound = len >= 2 ? len - 2 : 0; + for (int i = len - 1; i >= bound && b[i] == '='; --i) { + --len; + } + while (len >= 4) { + // Decode 4 bytes at a time + TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place + off += 4; + len -= 4; + size += 3; + } + // Don't decode if we hit the end or got a single leftover byte (invalid + // base64 but legal for skip of regular string type) + if (len > 1) { + // Decode remainder + TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place + size += len - 1; + } + // Sadly we must copy the byte[] (any way around this?) + byte[] result = new byte[size]; + System.arraycopy(b, 0, result, 0, size); + return result; + } + + private void readJSONObjectStart() throws TException { + context_.read(); + readJSONSyntaxChar(LBRACE); + pushContext(new JSONPairContext()); + } + + private void readJSONObjectEnd() throws TException { + readJSONSyntaxChar(RBRACE); + popContext(); + } + + private void readJSONArrayStart() throws TException { + context_.read(); + readJSONSyntaxChar(LBRACKET); + pushContext(new JSONListContext()); + } + + private void readJSONArrayEnd() throws TException { + readJSONSyntaxChar(RBRACKET); + popContext(); + } + + public TMessage readMessageBegin() throws TException { + readJSONArrayStart(); + if (readJSONInteger() != VERSION) { + throw new TProtocolException(TProtocolException.BAD_VERSION, + "Message contained bad version."); + } + String name; + try { + name = readJSONString(false).toString("UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + byte type = (byte)readJSONInteger(); + int seqid = (int)readJSONInteger(); + return new TMessage(name, type, seqid); + } + + public void readMessageEnd() throws TException { + readJSONArrayEnd(); + } + + public TStruct readStructBegin() throws TException { + readJSONObjectStart(); + return ANONYMOUS_STRUCT; + } + + public void readStructEnd() throws TException { + readJSONObjectEnd(); + } + + public TField readFieldBegin() throws TException { + byte ch = reader_.peek(); + byte type; + short id = 0; + if (ch == RBRACE[0]) { + type = TType.STOP; + } + else { + id = (short)readJSONInteger(); + readJSONObjectStart(); + type = getTypeIDForTypeName(readJSONString(false).get()); + } + return new TField("", type, id); + } + + public void readFieldEnd() throws TException { + readJSONObjectEnd(); + } + + public TMap readMapBegin() throws TException { + readJSONArrayStart(); + byte keyType = getTypeIDForTypeName(readJSONString(false).get()); + byte valueType = getTypeIDForTypeName(readJSONString(false).get()); + int size = (int)readJSONInteger(); + readJSONObjectStart(); + return new TMap(keyType, valueType, size); + } + + public void readMapEnd() throws TException { + readJSONObjectEnd(); + readJSONArrayEnd(); + } + + public TList readListBegin() throws TException { + readJSONArrayStart(); + byte elemType = getTypeIDForTypeName(readJSONString(false).get()); + int size = (int)readJSONInteger(); + return new TList(elemType, size); + } + + public void readListEnd() throws TException { + readJSONArrayEnd(); + } + + public TSet readSetBegin() throws TException { + readJSONArrayStart(); + byte elemType = getTypeIDForTypeName(readJSONString(false).get()); + int size = (int)readJSONInteger(); + return new TSet(elemType, size); + } + + public void readSetEnd() throws TException { + readJSONArrayEnd(); + } + + public boolean readBool() throws TException { + return (readJSONInteger() == 0 ? false : true); + } + + public byte readByte() throws TException { + return (byte)readJSONInteger(); + } + + public short readI16() throws TException { + return (short)readJSONInteger(); + } + + public int readI32() throws TException { + return (int)readJSONInteger(); + } + + public long readI64() throws TException { + return readJSONInteger(); + } + + public double readDouble() throws TException { + return readJSONDouble(); + } + + public String readString() throws TException { + try { + return readJSONString(false).toString("UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new TException("JVM DOES NOT SUPPORT UTF-8"); + } + } + + public byte[] readBinary() throws TException { + return readJSONBase64(); + } + +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java new file mode 100644 index 000000000..88f678561 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TList.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates list metadata. + * + */ +public class TList { + public TList() {} + + public TList(byte t, int s) { + elemType = t; + size = s; + } + + public byte elemType = TType.STOP; + public int size = 0; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java new file mode 100644 index 000000000..718a1193e --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMap.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates map metadata. + * + */ +public class TMap { + public TMap() {} + + public TMap(byte k, byte v, int s) { + keyType = k; + valueType = v; + size = s; + } + + public byte keyType = TType.STOP; + public byte valueType = TType.STOP; + public int size = 0; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java new file mode 100644 index 000000000..408b05a9a --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessage.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates struct metadata. + * + */ +public class TMessage { + public TMessage() {} + + public TMessage(String n, byte t, int s) { + name = n; + type = t; + seqid = s; + } + + public String name = ""; + public byte type; + public int seqid; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java new file mode 100644 index 000000000..714ea7091 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TMessageType.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Message type constants in the Thrift protocol. + * + */ +public final class TMessageType { + public static final byte CALL = 1; + public static final byte REPLY = 2; + public static final byte EXCEPTION = 3; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java new file mode 100644 index 000000000..710e6d4c1 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocol.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransport; + +/** + * Protocol interface definition. + * + */ +public abstract class TProtocol { + + /** + * Prevent direct instantiation + */ + private TProtocol() {} + + /** + * Transport + */ + protected TTransport trans_; + + /** + * Constructor + */ + protected TProtocol(TTransport trans) { + trans_ = trans; + } + + /** + * Transport accessor + */ + public TTransport getTransport() { + return trans_; + } + + /** + * Writing methods. + */ + + public abstract void writeMessageBegin(TMessage message) throws TException; + + public abstract void writeMessageEnd() throws TException; + + public abstract void writeStructBegin(TStruct struct) throws TException; + + public abstract void writeStructEnd() throws TException; + + public abstract void writeFieldBegin(TField field) throws TException; + + public abstract void writeFieldEnd() throws TException; + + public abstract void writeFieldStop() throws TException; + + public abstract void writeMapBegin(TMap map) throws TException; + + public abstract void writeMapEnd() throws TException; + + public abstract void writeListBegin(TList list) throws TException; + + public abstract void writeListEnd() throws TException; + + public abstract void writeSetBegin(TSet set) throws TException; + + public abstract void writeSetEnd() throws TException; + + public abstract void writeBool(boolean b) throws TException; + + public void writeBool(Boolean b) throws TException { + writeBool(b.booleanValue()); + } + + public abstract void writeByte(byte b) throws TException; + + public void writeByte(Byte b) throws TException { + writeByte(b.byteValue()); + } + + public abstract void writeI16(short i16) throws TException; + + public void writeI16(Short i16) throws TException { + writeI16(i16.shortValue()); + } + + public abstract void writeI32(int i32) throws TException; + + public void writeI32(Integer i32) throws TException { + writeI32(i32.intValue()); + } + + public abstract void writeI64(long i64) throws TException; + + public void writeI64(Long i64) throws TException { + writeI64(i64.longValue()); + } + + public abstract void writeDouble(double dub) throws TException; + + public void writeDouble(Double d) throws TException { + writeDouble(d.doubleValue()); + } + + public abstract void writeString(String str) throws TException; + + public abstract void writeBinary(byte[] bin) throws TException; + + /** + * Reading methods. + */ + + public abstract TMessage readMessageBegin() throws TException; + + public abstract void readMessageEnd() throws TException; + + public abstract TStruct readStructBegin() throws TException; + + public abstract void readStructEnd() throws TException; + + public abstract TField readFieldBegin() throws TException; + + public abstract void readFieldEnd() throws TException; + + public abstract TMap readMapBegin() throws TException; + + public abstract void readMapEnd() throws TException; + + public abstract TList readListBegin() throws TException; + + public abstract void readListEnd() throws TException; + + public abstract TSet readSetBegin() throws TException; + + public abstract void readSetEnd() throws TException; + + public abstract boolean readBool() throws TException; + + public abstract byte readByte() throws TException; + + public abstract short readI16() throws TException; + + public abstract int readI32() throws TException; + + public abstract long readI64() throws TException; + + public abstract double readDouble() throws TException; + + public abstract String readString() throws TException; + + public abstract byte[] readBinary() throws TException; + +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java new file mode 100644 index 000000000..248815bec --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolException.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; + +/** + * Protocol exceptions. + * + */ +public class TProtocolException extends TException { + + + private static final long serialVersionUID = 1L; + public static final int UNKNOWN = 0; + public static final int INVALID_DATA = 1; + public static final int NEGATIVE_SIZE = 2; + public static final int SIZE_LIMIT = 3; + public static final int BAD_VERSION = 4; + public static final int NOT_IMPLEMENTED = 5; + + protected int type_ = UNKNOWN; + + public TProtocolException() { + super(); + } + + public TProtocolException(int type) { + super(); + type_ = type; + } + + public TProtocolException(int type, String message) { + super(message); + type_ = type; + } + + public TProtocolException(String message) { + super(message); + } + + public TProtocolException(int type, Throwable cause) { + super(cause); + type_ = type; + } + + public TProtocolException(Throwable cause) { + super(cause); + } + + public TProtocolException(String message, Throwable cause) { + super(message, cause); + } + + public TProtocolException(int type, String message, Throwable cause) { + super(message, cause); + type_ = type; + } + + public int getType() { + return type_; + } + +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java new file mode 100644 index 000000000..afa502b70 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.transport.TTransport; + +/** + * Factory interface for constructing protocol instances. + * + */ +public interface TProtocolFactory { + public TProtocol getProtocol(TTransport trans); +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java new file mode 100644 index 000000000..c327448ef --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TProtocolUtil.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +import org.apache.thrift.TException; + +/** + * Utility class with static methods for interacting with protocol data + * streams. + * + */ +public class TProtocolUtil { + + /** + * The maximum recursive depth the skip() function will traverse before + * throwing a TException. + */ + private static int maxSkipDepth = Integer.MAX_VALUE; + + /** + * Specifies the maximum recursive depth that the skip function will + * traverse before throwing a TException. This is a global setting, so + * any call to skip in this JVM will enforce this value. + * + * @param depth the maximum recursive depth. A value of 2 would allow + * the skip function to skip a structure or collection with basic children, + * but it would not permit skipping a struct that had a field containing + * a child struct. A value of 1 would only allow skipping of simple + * types and empty structs/collections. + */ + public static void setMaxSkipDepth(int depth) { + maxSkipDepth = depth; + } + + /** + * Skips over the next data element from the provided input TProtocol object. + * + * @param prot the protocol object to read from + * @param type the next value will be intepreted as this TType value. + */ + public static void skip(TProtocol prot, byte type) + throws TException { + skip(prot, type, maxSkipDepth); + } + + /** + * Skips over the next data element from the provided input TProtocol object. + * + * @param prot the protocol object to read from + * @param type the next value will be intepreted as this TType value. + * @param maxDepth this function will only skip complex objects to this + * recursive depth, to prevent Java stack overflow. + */ + public static void skip(TProtocol prot, byte type, int maxDepth) + throws TException { + if (maxDepth <= 0) { + throw new TException("Maximum skip depth exceeded"); + } + switch (type) { + case TType.BOOL: + { + prot.readBool(); + break; + } + case TType.BYTE: + { + prot.readByte(); + break; + } + case TType.I16: + { + prot.readI16(); + break; + } + case TType.I32: + { + prot.readI32(); + break; + } + case TType.I64: + { + prot.readI64(); + break; + } + case TType.DOUBLE: + { + prot.readDouble(); + break; + } + case TType.STRING: + { + prot.readBinary(); + break; + } + case TType.STRUCT: + { + prot.readStructBegin(); + while (true) { + TField field = prot.readFieldBegin(); + if (field.type == TType.STOP) { + break; + } + skip(prot, field.type, maxDepth - 1); + prot.readFieldEnd(); + } + prot.readStructEnd(); + break; + } + case TType.MAP: + { + TMap map = prot.readMapBegin(); + for (int i = 0; i < map.size; i++) { + skip(prot, map.keyType, maxDepth - 1); + skip(prot, map.valueType, maxDepth - 1); + } + prot.readMapEnd(); + break; + } + case TType.SET: + { + TSet set = prot.readSetBegin(); + for (int i = 0; i < set.size; i++) { + skip(prot, set.elemType, maxDepth - 1); + } + prot.readSetEnd(); + break; + } + case TType.LIST: + { + TList list = prot.readListBegin(); + for (int i = 0; i < list.size; i++) { + skip(prot, list.elemType, maxDepth - 1); + } + prot.readListEnd(); + break; + } + default: + throw new TProtocolException(TProtocolException.INVALID_DATA, + "Unrecognized type " + type); + } + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java new file mode 100644 index 000000000..f4e9b1d34 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TSet.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates set metadata. + * + */ +public class TSet { + public TSet() {} + + public TSet(byte t, int s) { + elemType = t; + size = s; + } + + public byte elemType = TType.STOP; + public int size = 0; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java new file mode 100644 index 000000000..cecbbcfce --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TStruct.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Helper class that encapsulates struct metadata. + * + */ +public class TStruct { + public TStruct() {} + + public TStruct(String n) { + name = n; + } + + public String name = ""; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java new file mode 100644 index 000000000..dbdc3caa8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/protocol/TType.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.protocol; + +/** + * Type constants in the Thrift protocol. + * + */ +public final class TType { + public static final byte STOP = 0; + public static final byte VOID = 1; + public static final byte BOOL = 2; + public static final byte BYTE = 3; + public static final byte DOUBLE = 4; + public static final byte I16 = 6; + public static final byte I32 = 8; + public static final byte I64 = 10; + public static final byte STRING = 11; + public static final byte STRUCT = 12; + public static final byte MAP = 13; + public static final byte SET = 14; + public static final byte LIST = 15; +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java new file mode 100644 index 000000000..c83748ad2 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TFramedTransport.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.ByteArrayInputStream; + +import org.apache.thrift.TByteArrayOutputStream; + +/** + * Socket implementation of the TTransport interface. To be commented soon! + * + */ +public class TFramedTransport extends TTransport { + + /** + * Underlying transport + */ + private TTransport transport_ = null; + + /** + * Buffer for output + */ + private final TByteArrayOutputStream writeBuffer_ = + new TByteArrayOutputStream(1024); + + /** + * Buffer for input + */ + private ByteArrayInputStream readBuffer_ = null; + + public static class Factory extends TTransportFactory { + public Factory() { + } + + public TTransport getTransport(TTransport base) { + return new TFramedTransport(base); + } + } + + /** + * Constructor wraps around another tranpsort + */ + public TFramedTransport(TTransport transport) { + transport_ = transport; + } + + public void open() throws TTransportException { + transport_.open(); + } + + public boolean isOpen() { + return transport_.isOpen(); + } + + public void close() { + transport_.close(); + } + + public int read(byte[] buf, int off, int len) throws TTransportException { + if (readBuffer_ != null) { + int got = readBuffer_.read(buf, off, len); + if (got > 0) { + return got; + } + } + + // Read another frame of data + readFrame(); + + return readBuffer_.read(buf, off, len); + } + + private void readFrame() throws TTransportException { + byte[] i32rd = new byte[4]; + transport_.readAll(i32rd, 0, 4); + int size = + ((i32rd[0] & 0xff) << 24) | + ((i32rd[1] & 0xff) << 16) | + ((i32rd[2] & 0xff) << 8) | + ((i32rd[3] & 0xff)); + + byte[] buff = new byte[size]; + transport_.readAll(buff, 0, size); + readBuffer_ = new ByteArrayInputStream(buff); + } + + public void write(byte[] buf, int off, int len) throws TTransportException { + writeBuffer_.write(buf, off, len); + } + + public void flush() throws TTransportException { + byte[] buf = writeBuffer_.get(); + int len = writeBuffer_.len(); + writeBuffer_.reset(); + + byte[] i32out = new byte[4]; + i32out[0] = (byte)(0xff & (len >> 24)); + i32out[1] = (byte)(0xff & (len >> 16)); + i32out[2] = (byte)(0xff & (len >> 8)); + i32out[3] = (byte)(0xff & (len)); + transport_.write(i32out, 0, 4); + transport_.write(buf, 0, len); + transport_.flush(); + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java new file mode 100644 index 000000000..e6ffba4e5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/THttpClient.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.Hashtable; + +import javax.microedition.io.Connector; +import javax.microedition.io.HttpConnection; + +/** + * HTTP implementation of the TTransport interface. Used for working with a + * Thrift web services implementation. + * + */ +public class THttpClient extends TTransport { + + private String url_ = null; + + private final ByteArrayOutputStream requestBuffer_ = new ByteArrayOutputStream(); + + private InputStream inputStream_ = null; + + private HttpConnection connection = null; + + private int connectTimeout_ = 0; + + private int readTimeout_ = 0; + + private Hashtable customHeaders_ = null; + + public THttpClient(String url) throws TTransportException { + url_ = url; + } + + public void setConnectTimeout(int timeout) { + connectTimeout_ = timeout; + } + + public void setReadTimeout(int timeout) { + readTimeout_ = timeout; + } + + public void setCustomHeaders(Hashtable headers) { + customHeaders_ = headers; + } + + public void setCustomHeader(String key, String value) { + if (customHeaders_ == null) { + customHeaders_ = new Hashtable(); + } + customHeaders_.put(key, value); + } + + public void open() {} + + public void close() { + if (null != inputStream_) { + try { + inputStream_.close(); + } catch (IOException ioe) { + } + inputStream_ = null; + } + + if (connection != null) { + try { + connection.close(); + } catch (IOException ioe) { + } + connection = null; + } + } + + public boolean isOpen() { + return true; + } + + public int read(byte[] buf, int off, int len) throws TTransportException { + if (inputStream_ == null) { + throw new TTransportException("Response buffer is empty, no request."); + } + try { + int ret = inputStream_.read(buf, off, len); + if (ret == -1) { + throw new TTransportException("No more data available."); + } + return ret; + } catch (IOException iox) { + throw new TTransportException(iox); + } + } + + public void write(byte[] buf, int off, int len) { + requestBuffer_.write(buf, off, len); + } + + public void flush() throws TTransportException { + // Extract request and reset buffer + byte[] data = requestBuffer_.toByteArray(); + requestBuffer_.reset(); + + try { + // Create connection object + connection = (HttpConnection)Connector.open(url_); + + // Make the request + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/x-thrift"); + connection.setRequestProperty("Accept", "application/x-thrift"); + connection.setRequestProperty("User-Agent", "JavaME/THttpClient"); + + connection.setRequestProperty("Connection", "Keep-Alive"); + connection.setRequestProperty("Keep-Alive", "5000"); + connection.setRequestProperty("Http-version", "HTTP/1.1"); + connection.setRequestProperty("Cache-Control", "no-transform"); + + if (customHeaders_ != null) { + for (Enumeration e = customHeaders_.keys() ; e.hasMoreElements() ;) { + String key = (String)e.nextElement(); + String value = (String)customHeaders_.get(key); + connection.setRequestProperty(key, value); + } + } + + OutputStream os = connection.openOutputStream(); + os.write(data); + os.close(); + + int responseCode = connection.getResponseCode(); + if (responseCode != HttpConnection.HTTP_OK) { + throw new TTransportException("HTTP Response code: " + responseCode); + } + + // Read the responses + inputStream_ = connection.openInputStream(); + } catch (IOException iox) { + System.out.println(iox.toString()); + throw new TTransportException(iox); + } + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java new file mode 100644 index 000000000..ccf706461 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TIOStreamTransport.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This is the most commonly used base transport. It takes an InputStream + * and an OutputStream and uses those to perform all transport operations. + * This allows for compatibility with all the nice constructs Java already + * has to provide a variety of types of streams. + * + */ +public class TIOStreamTransport extends TTransport { + + + /** Underlying inputStream */ + protected InputStream inputStream_ = null; + + /** Underlying outputStream */ + protected OutputStream outputStream_ = null; + + /** + * Subclasses can invoke the default constructor and then assign the input + * streams in the open method. + */ + protected TIOStreamTransport() {} + + /** + * Input stream constructor. + * + * @param is Input stream to read from + */ + public TIOStreamTransport(InputStream is) { + inputStream_ = is; + } + + /** + * Output stream constructor. + * + * @param os Output stream to read from + */ + public TIOStreamTransport(OutputStream os) { + outputStream_ = os; + } + + /** + * Two-way stream constructor. + * + * @param is Input stream to read from + * @param os Output stream to read from + */ + public TIOStreamTransport(InputStream is, OutputStream os) { + inputStream_ = is; + outputStream_ = os; + } + + /** + * The streams must already be open at construction time, so this should + * always return true. + * + * @return true + */ + public boolean isOpen() { + return true; + } + + /** + * The streams must already be open. This method does nothing. + */ + public void open() throws TTransportException {} + + /** + * Closes both the input and output streams. + */ + public void close() { + if (inputStream_ != null) { + try { + inputStream_.close(); + } catch (IOException iox) { + } + inputStream_ = null; + } + if (outputStream_ != null) { + try { + outputStream_.close(); + } catch (IOException iox) { + } + outputStream_ = null; + } + } + + /** + * Reads from the underlying input stream if not null. + */ + public int read(byte[] buf, int off, int len) throws TTransportException { + if (inputStream_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot read from null inputStream"); + } + try { + return inputStream_.read(buf, off, len); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } + + /** + * Writes to the underlying output stream if not null. + */ + public void write(byte[] buf, int off, int len) throws TTransportException { + if (outputStream_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream"); + } + try { + outputStream_.write(buf, off, len); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } + + /** + * Flushes the underlying output stream if not null. + */ + public void flush() throws TTransportException { + if (outputStream_ == null) { + throw new TTransportException(TTransportException.NOT_OPEN, "Cannot flush null outputStream"); + } + try { + outputStream_.flush(); + } catch (IOException iox) { + throw new TTransportException(TTransportException.UNKNOWN, iox); + } + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java new file mode 100644 index 000000000..4ae1ab242 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TMemoryBuffer.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.apache.thrift.TByteArrayOutputStream; +import java.io.UnsupportedEncodingException; + +/** + * Memory buffer-based implementation of the TTransport interface. + */ +public class TMemoryBuffer extends TTransport { + /** + * Create a TMemoryBuffer with an initial buffer size of <i>size</i>. The + * internal buffer will grow as necessary to accommodate the size of the data + * being written to it. + */ + public TMemoryBuffer(int size) { + arr_ = new TByteArrayOutputStream(size); + } + + public boolean isOpen() { + return true; + } + + public void open() { + /* Do nothing */ + } + + public void close() { + /* Do nothing */ + } + + public int read(byte[] buf, int off, int len) { + byte[] src = arr_.get(); + int amtToRead = (len > arr_.len() - pos_ ? arr_.len() - pos_ : len); + if (amtToRead > 0) { + System.arraycopy(src, pos_, buf, off, amtToRead); + pos_ += amtToRead; + } + return amtToRead; + } + + public void write(byte[] buf, int off, int len) { + arr_.write(buf, off, len); + } + + /** + * Output the contents of the memory buffer as a String, using the supplied + * encoding + * + * @param enc the encoding to use + * @return the contents of the memory buffer as a String + */ + public String toString(String enc) throws UnsupportedEncodingException { + return arr_.toString(enc); + } + + public String inspect() { + String buf = ""; + byte[] bytes = arr_.toByteArray(); + for (int i = 0; i < bytes.length; i++) { + buf += (pos_ == i ? "==>" : "") + Integer.toHexString(bytes[i] & 0xff) + " "; + } + return buf; + } + + // The contents of the buffer + private TByteArrayOutputStream arr_; + + // Position to read next byte from + private int pos_; + + public int length() { + return arr_.size(); + } + + public byte[] getArray() { + return arr_.get(); + } +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java new file mode 100644 index 000000000..a6c047bb5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransport.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +/** + * Generic class that encapsulates the I/O layer. This is basically a thin + * wrapper around the combined functionality of Java input/output streams. + * + */ +public abstract class TTransport { + + /** + * Queries whether the transport is open. + * + * @return True if the transport is open. + */ + public abstract boolean isOpen(); + + /** + * Is there more data to be read? + * + * @return True if the remote side is still alive and feeding us + */ + public boolean peek() { + return isOpen(); + } + + /** + * Opens the transport for reading/writing. + * + * @throws TTransportException if the transport could not be opened + */ + public abstract void open() + throws TTransportException; + + /** + * Closes the transport. + */ + public abstract void close(); + + /** + * Reads up to len bytes into buffer buf, starting att offset off. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read + * @throws TTransportException if there was an error reading data + */ + public abstract int read(byte[] buf, int off, int len) + throws TTransportException; + + /** + * Guarantees that all of len bytes are actually read off the transport. + * + * @param buf Array to read into + * @param off Index to start reading at + * @param len Maximum number of bytes to read + * @return The number of bytes actually read, which must be equal to len + * @throws TTransportException if there was an error reading data + */ + public int readAll(byte[] buf, int off, int len) + throws TTransportException { + int got = 0; + int ret = 0; + while (got < len) { + ret = read(buf, off+got, len-got); + if (ret <= 0) { + throw new TTransportException("Cannot read. Remote side has closed. Tried to read " + len + " bytes, but only got " + got + " bytes."); + } + got += ret; + } + return got; + } + + /** + * Writes the buffer to the output + * + * @param buf The output data buffer + * @throws TTransportException if an error occurs writing data + */ + public void write(byte[] buf) throws TTransportException { + write(buf, 0, buf.length); + } + + /** + * Writes up to len bytes from the buffer. + * + * @param buf The output data buffer + * @param off The offset to start writing from + * @param len The number of bytes to write + * @throws TTransportException if there was an error writing data + */ + public abstract void write(byte[] buf, int off, int len) + throws TTransportException; + + /** + * Flush any pending data out of a transport buffer. + * + * @throws TTransportException if there was an error writing out data. + */ + public void flush() + throws TTransportException {} +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java new file mode 100644 index 000000000..d08f3b02b --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportException.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +import org.apache.thrift.TException; + +/** + * Transport exceptions. + * + */ +public class TTransportException extends TException { + + private static final long serialVersionUID = 1L; + + public static final int UNKNOWN = 0; + public static final int NOT_OPEN = 1; + public static final int ALREADY_OPEN = 2; + public static final int TIMED_OUT = 3; + public static final int END_OF_FILE = 4; + + protected int type_ = UNKNOWN; + + public TTransportException() { + super(); + } + + public TTransportException(int type) { + super(); + type_ = type; + } + + public TTransportException(int type, String message) { + super(message); + type_ = type; + } + + public TTransportException(String message) { + super(message); + } + + public TTransportException(int type, Throwable cause) { + super(cause); + type_ = type; + } + + public TTransportException(Throwable cause) { + super(cause); + } + + public TTransportException(String message, Throwable cause) { + super(message, cause); + } + + public TTransportException(int type, String message, Throwable cause) { + super(message, cause); + type_ = type; + } + + public int getType() { + return type_; + } + +} diff --git a/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java new file mode 100644 index 000000000..45b607f3e --- /dev/null +++ b/src/jaegertracing/thrift/lib/javame/src/org/apache/thrift/transport/TTransportFactory.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.thrift.transport; + +/** + * Factory class used to create wrapped instance of Transports. + * This is used primarily in servers, which get Transports from + * a ServerTransport and then may want to mutate them (i.e. create + * a BufferedTransport from the underlying base transport) + * + */ +public class TTransportFactory { + + /** + * Return a wrapped instance of the base Transport. + * + * @param in The base transport + * @returns Wrapped Transport + */ + public TTransport getTransport(TTransport trans) { + return trans; + } + +} |