Moving from govendor to dep, updated dependencies (#48)

* Moving from govendor to dep.

* Making the pull request template more friendly.

* Fixing akward space in PR template.

* goimports run on whole project using ` goimports -w $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./gen-go/*")`

source of command: https://gist.github.com/bgentry/fd1ffef7dbde01857f66
This commit is contained in:
Renan DelValle 2018-01-07 13:13:47 -08:00 committed by GitHub
parent 9631aa3aab
commit 8d445c1c77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2186 changed files with 400410 additions and 352 deletions

View file

@ -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.
#
file(GLOB java_sources src/**/*.java)
if(ANDROID)
set(android_sources
android/build.gradle
android/settings.gradle
android/src/main/AndroidManifest.xml
)
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}" assemble
DEPENDS ${java_sources} ${android_sources})
add_custom_target(thrift_aar ALL DEPENDS ${THRIFT_AAR})
else(ANDROID)
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()
file(GLOB_RECURSE ThriftJava_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.java)
set(ThriftJava_ARTIFACTS
libthrift-${thrift_VERSION}.jar
libthrift-${thrift_VERSION}.pom
)
add_custom_command(
OUTPUT ${ThriftJava_ARTIFACTS}
COMMAND ${Ant_EXECUTABLE} ${ANT_FLAGS} -Dbuild.dir="${CMAKE_CURRENT_BINARY_DIR}" -f build.xml
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/build.xml
DEPENDS ${ThriftJava_SOURCES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(ThriftJava ALL
COMMENT "Building Java library using Ant"
DEPENDS ${ThriftJava_ARTIFACTS}
)
# Hook the ant install task into CMake install
install(CODE "execute_process(
COMMAND ${Ant_EXECUTABLE} ${ANT_FLAGS} install
-Dbuild.dir=\"${CMAKE_CURRENT_BINARY_DIR}\"
-Dinstall.path=\"${JAVA_INSTALL_DIR}\" -Dinstall.javadoc.path=\"${JAVA_DOC_INSTALL_DIR}\" -f build.xml
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)")
if(BUILD_TESTING)
add_test(NAME JavaTest
COMMAND ${Ant_EXECUTABLE} ${ANT_FLAGS} -Dbuild.dir="${CMAKE_CURRENT_BINARY_DIR}" -Dthrift.compiler="${THRIFT_COMPILER}" -f build.xml test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
endif()
endif(ANDROID)

50
vendor/git.apache.org/thrift.git/lib/java/Makefile.am generated vendored Normal file
View file

@ -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.
#
export CLASSPATH
THRIFT = $(top_builddir)/compiler/cpp/thrift
all-local:
$(ANT) $(ANT_FLAGS)
install-exec-hook:
$(ANT) $(ANT_FLAGS) install -Dinstall.path=$(DESTDIR)$(JAVA_PREFIX) \
-Dinstall.javadoc.path=$(DESTDIR)$(docdir)/java
# Make sure this doesn't fail if ant is not configured.
clean-local:
ANT=$(ANT) ; if test -z "$$ANT" ; then ANT=: ; fi ; \
$$ANT $(ANT_FLAGS) clean
precross: $(THRIFT)
$(ANT) $(ANT_FLAGS) compile-test
check-local: all
$(ANT) $(ANT_FLAGS) test
EXTRA_DIST = \
build.xml \
build.properties \
CMakeLists.txt \
coding_standards.md \
android \
src \
test \
README.md

53
vendor/git.apache.org/thrift.git/lib/java/README.md generated vendored Normal file
View file

@ -0,0 +1,53 @@
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.
Using Thrift with Java
======================
The Thrift Java source is not build using the GNU tools, but rather uses
the Apache Ant build system, which tends to be predominant amongst Java
developers.
To compile the Java Thrift libraries, simply do the following:
ant
Yep, that's easy. Look for libthrift.jar in the base directory.
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:
ant -Dproxy.enabled=1 -Dproxy.host=myproxyhost -Dproxy.user=thriftuser -Dproxy.pass=topsecret
or via
./configure --with-java ANT_FLAGS='-Dproxy.enabled=1 -Dproxy.host=myproxyhost -Dproxy.user=thriftuser -Dproxy.pass=topsecret'
Dependencies
============
Apache Ant
http://ant.apache.org/

View file

@ -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'
}
}

View file

@ -0,0 +1 @@
rootProject.name='thrift'

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.apache.thrift">
<application />
</manifest>

View file

@ -0,0 +1,31 @@
thrift.version=0.10.0
thrift.groupid=org.apache.thrift
release=false
# Jar Versions
mvn.ant.task.version=2.1.3
# Local Install paths
install.path=/usr/local/lib
install.javadoc.path=${install.path}
# Maven dependency download locations
mvn.repo=http://repo1.maven.org/maven2
apache.repo=https://repository.apache.org/content/repositories/releases
mvn.ant.task.url=${mvn.repo}/org/apache/maven/maven-ant-tasks/${mvn.ant.task.version}
mvn.ant.task.jar=maven-ant-tasks-${mvn.ant.task.version}.jar
# 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
# Jar Versions
mvn.ant.task.version=2.1.3
# Dependency versions
httpclient.version=4.4.1
httpcore.version=4.4.1
slf4j.version=1.7.12
servlet.version=2.5

414
vendor/git.apache.org/thrift.git/lib/java/build.xml generated vendored Normal file
View file

@ -0,0 +1,414 @@
<?xml version="1.0"?>
<!--
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.
-->
<project name="libthrift" default="dist" basedir="."
xmlns:artifact="antlib:org.apache.maven.artifact.ant">
<description>Thrift Build File</description>
<property name="thrift.root" location="../../"/>
<property name="thrift.artifactid" value="libthrift"/>
<!-- Include the base properties file -->
<property file="${basedir}/build.properties" />
<property environment="env"/>
<condition property="version" value="${thrift.version}">
<isset property="release"/>
</condition>
<property name="version" value="${thrift.version}-snapshot"/>
<property name="final.name" value="${thrift.artifactid}-${version}"/>
<property name="thrift.compiler" location="../../compiler/cpp/thrift"/>
<property name="src" location="${basedir}/src"/>
<property name="build.dir" location="${basedir}/build"/>
<property name="build.lib.dir" location="${build.dir}/lib"/>
<property name="build.tools.dir" location="${build.dir}/tools"/>
<property name="src.test" location="test"/>
<property name="javadoc.dir" location="${build.dir}/javadoc"/>
<property name="build.test.dir" location="${build.dir}/test"/>
<property name="test.thrift.home" location="${thrift.root}/test"/>
<property name="gen" location="gen-java"/>
<property name="genbean" location="gen-javabean"/>
<property name="genreuse" location="gen-javareuse"/>
<property name="genfullcamel" location="gen-fullcamel"/>
<property name="jar.file" location="${build.dir}/${final.name}.jar"/>
<property name="test.jar.file" location="${build.dir}/${final.name}-test.jar"/>
<property name="javadoc.jar.file" location="${build.dir}/${final.name}-javadoc.jar"/>
<property name="source.jar.file" location="${build.dir}/${final.name}-sources.jar"/>
<!-- Junit properties -->
<property name="test.junit.output.format" value="plain"/>
<property name="test.timeout" value="2000000"/>
<property name="test.src.dir" location="${basedir}/test"/>
<property name="test.log.dir" value="${build.test.dir}/log"/>
<property name="test.port" value="9090"/>
<!-- maven properties -->
<property name="pom.xml" location="${build.dir}/${final.name}.pom"/>
<path id="compile.classpath">
<fileset dir="${build.lib.dir}">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="test.classpath">
<path refid="compile.classpath"/>
<pathelement path="${env.CLASSPATH}"/>
<pathelement location="${build.test.dir}"/>
<pathelement location="${jar.file}"/>
<pathelement location="${test.jar.file}"/>
</path>
<!-- Tasks -->
<target name="init" depends="setup.init,mvn.init" unless="init.finished">
<property name="init.finished" value="true"/>
</target>
<target name="setup.init">
<tstamp/>
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.lib.dir}"/>
<mkdir dir="${build.tools.dir}"/>
<mkdir dir="${build.test.dir}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build.dir}" source="1.5" target="1.5"
debug="true" classpathref="compile.classpath" includeantruntime="false" encoding="UTF-8">
<compilerarg value="-Xlint:unchecked"/>
</javac>
</target>
<target name="javadoc" depends="init">
<javadoc sourcepath="${src}" destdir="${javadoc.dir}"
version="true" windowtitle="Thrift Java API" doctitle="Thrift Java API"
classpathref="test.classpath">
</javadoc>
<jar jarfile="${javadoc.jar.file}" basedir="${javadoc.dir}">
<manifest>
<attribute name="Implementation-Version" value="${version}"/>
</manifest>
</jar>
</target>
<target name="dist" depends="compile">
<mkdir dir="${build.dir}/META-INF"/>
<copy file="${thrift.root}/LICENSE" tofile="${build.dir}/META-INF/LICENSE.txt"/>
<copy file="${thrift.root}/NOTICE" tofile="${build.dir}/META-INF/NOTICE.txt"/>
<jar jarfile="${jar.file}">
<manifest>
<attribute name="Implementation-Version" value="${version}"/>
<attribute name="Bundle-ManifestVersion" value="2"/>
<attribute name="Bundle-SymbolicName" value="${thrift.groupid}"/>
<attribute name="Bundle-Name" value="Apache Thrift"/>
<attribute name="Bundle-Version" value="${version}"/>
<attribute name="Bundle-Description" value="Apache Thrift library"/>
<attribute name="Bundle-License" value="${license}"/>
<attribute name="Bundle-ActivationPolicy" value="lazy"/>
<attribute name="Export-Package" value="${thrift.groupid}.async;uses:=&quot;${thrift.groupid}.protocol,${thrift.groupid}.transport,org.slf4j,${thrift.groupid}&quot;;version=&quot;${version}&quot;,${thrift.groupid}.protocol;uses:=&quot;${thrift.groupid}.transport,${thrift.groupid},${thrift.groupid}.scheme&quot;;version=&quot;${version}&quot;,${thrift.groupid}.server;uses:=&quot;${thrift.groupid}.transport,${thrift.groupid}.protocol,${thrift.groupid},org.slf4j,javax.servlet,javax.servlet.http&quot;;version=&quot;${version}&quot;,${thrift.groupid}.transport;uses:=&quot;${thrift.groupid}.protocol,${thrift.groupid},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&quot;;version=&quot;${version}&quot;,${thrift.groupid};uses:=&quot;${thrift.groupid}.protocol,${thrift.groupid}.async,${thrift.groupid}.server,${thrift.groupid}.transport,org.slf4j,org.apache.log4j,${thrift.groupid}.scheme&quot;;version=&quot;${version}&quot;,${thrift.groupid}.meta_data;uses:=&quot;${thrift.groupid}&quot;;version=&quot;${version}&quot;,${thrift.groupid}.scheme;uses:=&quot;${thrift.groupid}.protocol,${thrift.groupid}&quot;;version=&quot;${version}&quot;"/>
<attribute name="Import-Package" value="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=&quot;[1.4,2)&quot;,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"/>
</manifest>
<fileset dir="${build.dir}">
<include name="org/apache/thrift/**/*.class"/>
<include name="META-INF/*.txt"/>
</fileset>
</jar>
</target>
<target name="pack.src">
<jar jarfile="${source.jar.file}">
<fileset dir="${src}">
<include name="**/*.java"/>
</fileset>
</jar>
</target>
<target name="install" depends="dist,javadoc">
<copy todir="${install.path}">
<fileset dir="${build.dir}" includes="*.jar"/>
<fileset dir="${build.lib.dir}" includes="*.jar"/>
</copy>
<copy todir="${install.javadoc.path}">
<fileset dir="${javadoc.dir}" includes="**/*"/>
</copy>
</target>
<target name="clean">
<delete dir="${build.dir}"/>
<delete dir="${gen}"/>
<delete dir="${genbean}"/>
<delete dir="${genreuse}"/>
<delete dir="${genfullcamel}"/>
</target>
<target name="optional-generate" unless="no-gen-thrift">
<antcall target="generate">
</antcall>
</target>
<target name="compile-test" description="Build the test suite classes" depends="optional-generate,dist">
<javac debug="true" srcdir="${gen}" destdir="${build.test.dir}" classpathref="test.classpath" includeantruntime="false" encoding="UTF-8">
<compilerarg value="-Xlint:deprecation"/>
<!--<compilerarg value="-Xlint:unchecked"/>-->
</javac>
<javac debug="true" srcdir="${genbean}" destdir="${build.test.dir}" classpathref="test.classpath" includeantruntime="false" encoding="UTF-8">
<compilerarg value="-Xlint:deprecation"/>
<!--<compilerarg value="-Xlint:unchecked"/>-->
</javac>
<javac debug="true" srcdir="${genreuse}" destdir="${build.test.dir}" classpathref="test.classpath" includeantruntime="false" encoding="UTF-8">
<compilerarg value="-Xlint:deprecation"/>
<!--<compilerarg value="-Xlint:unchecked"/>-->
</javac>
<javac debug="true" srcdir="${genfullcamel}" destdir="${build.test.dir}" classpathref="test.classpath" includeantruntime="false" encoding="UTF-8">
<compilerarg value="-Xlint:deprecation"/>
<!--<compilerarg value="-Xlint:unchecked"/>-->
</javac>
<javac debug="true" srcdir="${src.test}" destdir="${build.test.dir}" classpathref="test.classpath" includeantruntime="false" encoding="UTF-8">
<compilerarg value="-Xlint:deprecation"/>
<!--<compilerarg value="-Xlint:unchecked"/>-->
</javac>
<copy todir="${build.test.dir}">
<fileset dir="${src.test}" includes="log4j.properties"/>
</copy>
<jar jarfile="${test.jar.file}" basedir="${build.test.dir}"/>
</target>
<target name="junit-test" description="Run the JUnit test suite" depends="compile-test">
<mkdir dir="${test.log.dir}"/>
<junit printsummary="true" showoutput="${test.output}" timeout="${test.timeout}"
haltonfailure="true" errorProperty="tests.failed" failureProperty="tests.failed"
fork="true" forkmode="perTest" maxmemory="512m"
>
<sysproperty key="build.test" value="${build.test.dir}"/>
<sysproperty key="test.port" value="${test.port}"/>
<sysproperty key="javax.net.ssl.trustStore" value="${src.test}/.truststore"/>
<sysproperty key="javax.net.ssl.trustStorePassword" value="thrift"/>
<sysproperty key="javax.net.ssl.keyStore" value="${src.test}/.keystore"/>
<sysproperty key="javax.net.ssl.keyStorePassword" value="thrift"/>
<classpath refid="test.classpath"/>
<formatter type="${test.junit.output.format}"/>
<batchtest todir="${test.log.dir}" unless="testcase">
<fileset dir="${test.src.dir}">
<include name="**/Test*.java"/>
<exclude name="**/TestClient.java"/>
<exclude name="**/TestServer.java"/>
<exclude name="**/TestNonblockingServer.java"/>
</fileset>
</batchtest>
<batchtest todir="${test.log.dir}" if="testcase">
<fileset dir="${test.src.dir}" includes="**/${testcase}.java"/>
</batchtest>
</junit>
<fail if="tests.failed">Tests failed!</fail>
</target>
<target name="deprecated-test" description="Run the non-JUnit test suite" depends="compile-test">
<java classname="org.apache.thrift.test.EqualityTest" classpathref="test.classpath" failonerror="true"/>
<java classname="org.apache.thrift.test.JavaBeansTest" classpathref="test.classpath" failonerror="true"/>
</target>
<target name="test" description="Run the full test suite" depends="junit-test,deprecated-test"/>
<target name="testclient" description="Run a test client" depends="compile-test, run-testclient"/>
<target name="run-testclient" description="Run a test client">
<java classname="org.apache.thrift.test.TestClient"
classpathref="test.classpath" failonerror="true" fork="true">
<sysproperty key="javax.net.ssl.trustStore" value="${src.test}/.truststore"/>
<sysproperty key="javax.net.ssl.trustStorePassword" value="thrift"/>
<arg line="${testargs}"/>
</java>
</target>
<target name="testserver" description="Run a test server" depends="compile-test, run-testserver"/>
<target name="run-testserver" description="Run a test server">
<java classname="org.apache.thrift.test.TestServer"
classpathref="test.classpath" failonerror="true" fork="true">
<sysproperty key="javax.net.ssl.keyStore" value="${src.test}/.keystore"/>
<sysproperty key="javax.net.ssl.keyStorePassword" value="thrift"/>
<arg line="${testargs}"/>
</java>
</target>
<target name="testnonblockingserver" description="Run a test nonblocking server" depends="compile-test, run-testnonblockingserver"/>
<target name="run-testnonblockingserver" description="Run a test nonblocking server">
<java classname="org.apache.thrift.test.TestNonblockingServer"
classpathref="test.classpath" failonerror="true">
<arg line="${testargs}"/>
</java>
</target>
<target name="generate">
<!-- Generate the thrift gen-java source -->
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java ${test.thrift.home}/ThriftTest.thrift"/>
</exec>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java ${test.thrift.home}/JavaTypes.thrift"/>
</exec>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java ${test.thrift.home}/DebugProtoTest.thrift"/>
</exec>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java ${test.thrift.home}/OptionalRequiredTest.thrift"/>
</exec>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java:beans,nocamel ${test.thrift.home}/JavaBeansTest.thrift"/>
</exec>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java ${test.thrift.home}/ManyOptionals.thrift"/>
</exec>
<mkdir dir="${genfullcamel}"/>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java:fullcamel -out ${genfullcamel} ${test.thrift.home}/FullCamelTest.thrift"/>
</exec>
<mkdir dir="${genreuse}"/>
<exec executable="${thrift.compiler}" failonerror="true">
<arg line="--gen java:reuse-objects -out ${genreuse} ${test.thrift.home}/ReuseObjects.thrift"/>
</exec>
</target>
<target name="proxy" if="proxy.enabled">
<setproxy proxyhost="${proxy.host}" proxyport="${proxy.port}"
proxyuser="${proxy.user}" proxypassword="${proxy.pass}"/>
</target>
<target name="mvn.ant.tasks.download" depends="setup.init,mvn.ant.tasks.check,proxy" unless="mvn.ant.tasks.found">
<get src="${mvn.ant.task.url}/${mvn.ant.task.jar}" dest="${build.tools.dir}/${mvn.ant.task.jar}" usetimestamp="true"/>
</target>
<target name="mvn.ant.tasks.check">
<condition property="mvn.ant.tasks.found">
<typefound uri="antlib:org.apache.maven.artifact.ant" name="artifact"/>
</condition>
</target>
<target name="mvn.init" depends="mvn.ant.tasks.download" unless="mvn.finished">
<!-- Download mvn ant tasks, download dependencies, and setup pom file -->
<typedef uri="antlib:org.apache.maven.artifact.ant" classpath="${build.tools.dir}/${mvn.ant.task.jar}"/>
<!-- remote repositories used to download dependencies from -->
<artifact:remoteRepository id="central" url="${mvn.repo}"/>
<artifact:remoteRepository id="apache" url="${apache.repo}"/>
<!-- Pom file information -->
<artifact:pom id="pom"
groupId="${thrift.groupid}"
artifactId="${thrift.artifactid}"
version="${version}"
url="http://thrift.apache.org"
name="Apache Thrift"
description="Thrift is a software framework for scalable cross-language services development."
packaging="pom"
>
<remoteRepository refid="central"/>
<remoteRepository refid="apache"/>
<license name="The Apache Software License, Version 2.0" url="${license}"/>
<scm connection="scm:git:https://git-wip-us.apache.org/repos/asf/thrift.git"
developerConnection="scm:git:https://git-wip-us.apache.org/repos/asf/thrift.git"
url="https://git-wip-us.apache.org/repos/asf?p=thrift.git"
/>
<!-- Thrift Developers -->
<developer id="mcslee" name="Mark Slee"/>
<developer id="dreiss" name="David Reiss"/>
<developer id="aditya" name="Aditya Agarwal"/>
<developer id="marck" name="Marc Kwiatkowski"/>
<developer id="jwang" name="James Wang"/>
<developer id="cpiro" name="Chris Piro"/>
<developer id="bmaurer" name="Ben Maurer"/>
<developer id="kclark" name="Kevin Clark"/>
<developer id="jake" name="Jake Luciani"/>
<developer id="bryanduxbury" name="Bryan Duxbury"/>
<developer id="esteve" name="Esteve Fernandez"/>
<developer id="todd" name="Todd Lipcon"/>
<developer id="geechorama" name="Andrew McGeachie"/>
<developer id="molinaro" name="Anthony Molinaro"/>
<developer id="roger" name="Roger Meier"/>
<developer id="jfarrell" name="Jake Farrell"/>
<developer id="jensg" name="Jens Geyer"/>
<developer id="carl" name="Carl Yeksigian"/>
<!-- Thrift dependencies list -->
<dependency groupId="org.slf4j" artifactId="slf4j-api" version="${slf4j.version}"/>
<dependency groupId="javax.servlet" artifactId="servlet-api" version="${servlet.version}" scope="provided"/>
<dependency groupId="org.apache.httpcomponents" artifactId="httpclient" version="${httpclient.version}"/>
<dependency groupId="org.apache.httpcomponents" artifactId="httpcore" version="${httpcore.version}"/>
</artifact:pom>
<!-- Generate the pom file -->
<artifact:writepom pomRefId="pom" file="${pom.xml}"/>
<!-- Download the dependencies -->
<artifact:dependencies filesetId="build-dependency-jars" pomRefId="pom"/>
<!-- Copy the dependencies to the build/lib dir -->
<copy todir="${build.dir}/lib">
<fileset refid="build-dependency-jars"/>
<mapper type="flatten"/>
</copy>
<!-- Dependencies needed for testing -->
<artifact:dependencies filesetId="test-dependency-jars" useScope="runtime">
<dependency groupId="org.slf4j" artifactId="slf4j-log4j12" version="${slf4j.version}"/>
<dependency groupId="junit" artifactId="junit" version="4.4"/>
</artifact:dependencies>
<!-- Copy the test dependencies to the build/lib dir -->
<copy todir="${build.dir}/lib">
<fileset refid="test-dependency-jars"/>
<mapper type="flatten"/>
</copy>
<property name="mvn.finished" value="true"/>
</target>
<macrodef name="signAndDeploy">
<!-- Sign and deploy jars to apache repo -->
<attribute name="file"/>
<attribute name="classifier" default=""/>
<attribute name="packaging" default="jar"/>
<attribute name="pom" default=""/>
<sequential>
<artifact:mvn fork="true">
<arg value="org.apache.maven.plugins:maven-gpg-plugin:1.6:sign-and-deploy-file"/>
<arg value="-DrepositoryId=${maven-repository-id}"/>
<arg value="-Durl=${maven-repository-url}"/>
<arg value="-DpomFile=@{pom}"/>
<arg value="-Dfile=@{file}"/>
<arg value="-Dclassifier=@{classifier}"/>
<arg value="-Dpackaging=@{packaging}"/>
<arg value="-Pgpg"/>
</artifact:mvn>
</sequential>
</macrodef>
<target name="publish" depends="clean,init,test,dist,javadoc,pack.src">
<!-- Compile, package, test and then send release to apache maven repo -->
<!-- run with: ant -Drelease=true publish-->
<signAndDeploy file="${pom.xml}" packaging="pom" classifier="" pom="${pom.xml}"/>
<signAndDeploy file="${jar.file}" classifier="" pom="${pom.xml}"/>
<signAndDeploy file="${javadoc.jar.file}" classifier="javadoc" pom="${pom.xml}"/>
<signAndDeploy file="${source.jar.file}" classifier="sources" pom="${pom.xml}"/>
</target>
</project>

View file

@ -0,0 +1 @@
Please follow [General Coding Standards](/doc/coding_standards.md)

View file

@ -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 org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TMessageType;
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();
}
}

View file

@ -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);
}
}

View file

@ -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;
/**
* Implementation of the Option type pattern
*/
public abstract class Option<T> {
/**
* 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.toString()+")";
}
}
/**
* 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 new Some<T>(value);
} else {
return new None<T>();
}
}
/**
* 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);
}
public static <T> None<T> none() {
return new None<T>();
}
}

View file

@ -0,0 +1,70 @@
/**
*
*/
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.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();
TBase result = null;
try {
result = getResult(iface, args);
} catch(TException tex) {
LOGGER.error("Internal error processing " + getMethodName(), tex);
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();
}
return;
}
if(!isOneway()) {
oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.REPLY, seqid));
result.write(oprot);
oprot.writeMessageEnd();
oprot.getTransport().flush();
}
}
protected abstract boolean isOneway();
public abstract TBase getResult(I iface, T args) throws TException;
public abstract T getEmptyArgsInstance();
public String getMethodName() {
return methodName;
}
}

View file

@ -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;
/**
* 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.
*/
public class ShortStack {
private short[] vector;
private int top = -1;
public ShortStack(int initialCapacity) {
vector = new short[initialCapacity];
}
public short pop() {
return vector[top--];
}
public void push(short pushed) {
if (vector.length == top + 1) {
grow();
}
vector[++top] = pushed;
}
private void grow() {
short[] newVector = new short[vector.length * 2];
System.arraycopy(vector, 0, newVector, 0, vector.length);
vector = newVector;
}
public short peek() {
return vector[top];
}
public void clear() {
top = -1;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("<ShortStack vector:[");
for (int i = 0; i < vector.length; i++) {
if (i != 0) {
sb.append(" ");
}
if (i == top) {
sb.append(">>");
}
sb.append(vector[i]);
if (i == top) {
sb.append("<<");
}
}
sb.append("]>");
return sb.toString();
}
}

View file

@ -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();
}
}

View file

@ -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;
import org.apache.thrift.protocol.*;
import org.apache.thrift.server.AbstractNonblockingServer.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Map;
public interface TAsyncProcessor {
/**
* Implementations must call fb.responseReady() once processing is complete
*/
public boolean process(final AsyncFrameBuffer fb) throws TException;
}

View file

@ -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();
}

View file

@ -0,0 +1,105 @@
/*
* 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 boolean 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();
if (!fn.isOneway()) {
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();
}
fb.responseReady();
return true;
}
//Get Args
TBase args = fn.getEmptyArgsInstance();
try {
args.read(in);
} catch (TProtocolException e) {
in.readMessageEnd();
if (!fn.isOneway()) {
TApplicationException x = new TApplicationException(TApplicationException.PROTOCOL_ERROR, e.getMessage());
out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));
x.write(out);
out.writeMessageEnd();
out.getTransport().flush();
}
fb.responseReady();
return true;
}
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) {
resultHandler.onError(e);
}
return true;
}
@Override
public boolean process(TProtocol in, TProtocol out) throws TException {
return false;
}
}

View file

@ -0,0 +1,337 @@
/**
* 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.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Collection;
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.valueOf(a).compareTo(b);
}
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[i], b[i]);
if (byteCompare != 0) {
return byteCompare;
}
}
return 0;
}
public static int compareTo(Comparable a, Comparable b) {
return a.compareTo(b);
}
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++) {
lastComparison = comparator.compare(a.get(i), b.get(i));
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public static int compareTo(Set a, Set b) {
int lastComparison = compareTo(a.size(), b.size());
if (lastComparison != 0) {
return lastComparison;
}
SortedSet sortedA = new TreeSet(comparator);
sortedA.addAll(a);
SortedSet sortedB = new TreeSet(comparator);
sortedB.addAll(b);
Iterator iterA = sortedA.iterator();
Iterator iterB = sortedB.iterator();
// Compare each item.
while (iterA.hasNext() && iterB.hasNext()) {
lastComparison = comparator.compare(iterA.next(), iterB.next());
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
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) {
if (orig == null) {
return null;
}
byte[] copy = new byte[orig.length];
System.arraycopy(orig, 0, copy, 0, orig.length);
return copy;
}
public static int hashCode(long value) {
int low = (int) value;
int high = (int) (value >>> 32);
return high * 127 + low;
}
public static int hashCode(double value) {
return hashCode(Double.doubleToRawLongBits(value));
}
}

View file

@ -0,0 +1,42 @@
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 boolean 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();
return true;
}
fn.process(msg.seqid, in, out, iface);
return true;
}
}

View file

@ -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.io.ByteArrayOutputStream;
/**
* 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;
}
}

View file

@ -0,0 +1,358 @@
/*
* 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 this point is reached, iprot will be positioned at the start of the field.
switch(ttype){
case TType.BOOL:
if (field.type == TType.BOOL){
return protocol_.readBool();
}
break;
case TType.BYTE:
if (field.type == TType.BYTE) {
return protocol_.readByte();
}
break;
case TType.DOUBLE:
if (field.type == TType.DOUBLE) {
return protocol_.readDouble();
}
break;
case TType.I16:
if (field.type == TType.I16) {
return protocol_.readI16();
}
break;
case TType.I32:
if (field.type == TType.I32) {
return protocol_.readI32();
}
break;
case TType.I64:
if (field.type == TType.I64) {
return protocol_.readI64();
}
break;
case TType.STRING:
if (field.type == TType.STRING) {
return protocol_.readString();
}
break;
case 100: // hack to differentiate between string and binary
if (field.type == TType.STRING) {
return protocol_.readBinary();
}
break;
}
}
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;
for (int i = 0; i < fieldIdPathRest.length; i++){
fieldIdPath[i + 1] = fieldIdPathRest[i];
}
// 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());
}
}

View file

@ -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();
}

View file

@ -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.lang.InstantiationException;
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;
}
}
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -0,0 +1,143 @@
/*
* 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>();
/**
* '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);
}
/**
* 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 TException 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 boolean 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) {
// TODO Apache Guys - Can the server ever get an EXCEPTION or REPLY?
// TODO Should we check for this here?
throw new TException("This should not have happened!?");
}
// Extract the service name
int index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR);
if (index < 0) {
throw new TException("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 TException("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
return 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;
}
}
}

View file

@ -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());
}
}
}
}

View file

@ -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; }
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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));
}
}

View file

@ -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;
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;
}
System.out.format("Received %d%n", msg.seqid);
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();
}
}

View file

@ -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);
}

View file

@ -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();
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();
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);
}
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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());
}
}
}
}

View file

@ -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 &lt;Exception_1&gt;, &lt;Exception_2&gt;, ...</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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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]);
}
}
}
}

View file

@ -0,0 +1,426 @@
/*
* 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 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;
}
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);
}
public void writeByte(byte b) throws TException {
inoutTemp[0] = b;
trans_.write(inoutTemp, 0, 1);
}
public void writeI16(short i16) throws TException {
inoutTemp[0] = (byte)(0xff & (i16 >> 8));
inoutTemp[1] = (byte)(0xff & (i16));
trans_.write(inoutTemp, 0, 2);
}
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);
}
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);
}
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(ByteBuffer bin) throws TException {
int length = bin.limit() - bin.position();
writeI32(length);
trans_.write(bin.array(), bin.position() + bin.arrayOffset(), length);
}
/**
* Reading methods.
*/
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());
}
}
public void readMessageEnd() {}
public TStruct readStructBegin() {
return ANONYMOUS_STRUCT;
}
public void readStructEnd() {}
public TField readFieldBegin() throws TException {
byte type = readByte();
short id = type == TType.STOP ? 0 : readI16();
return new TField("", type, id);
}
public void readFieldEnd() {}
public TMap readMapBegin() throws TException {
TMap map = new TMap(readByte(), readByte(), readI32());
checkContainerReadLength(map.size);
return map;
}
public void readMapEnd() {}
public TList readListBegin() throws TException {
TList list = new TList(readByte(), readI32());
checkContainerReadLength(list.size);
return list;
}
public void readListEnd() {}
public TSet readSetBegin() throws TException {
TSet set = new TSet(readByte(), readI32());
checkContainerReadLength(set.size);
return set;
}
public void readSetEnd() {}
public boolean readBool() throws TException {
return (readByte() == 1);
}
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];
}
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)));
}
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));
}
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));
}
public double readDouble() throws TException {
return Double.longBitsToDouble(readI64());
}
public String readString() throws TException {
int size = readI32();
checkStringReadLength(size);
if (trans_.getBytesRemainingInBuffer() >= size) {
try {
String s = new String(trans_.getBuffer(), trans_.getBufferPosition(), size, "UTF-8");
trans_.consumeBuffer(size);
return s;
} catch (UnsupportedEncodingException e) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
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 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);
}
}

View file

@ -0,0 +1,908 @@
/*
* 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 org.apache.thrift.ShortStack;
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.
*/
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.
*/
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 (&lt; 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 {
try {
byte[] bytes = str.getBytes("UTF-8");
writeBinary(bytes, 0, bytes.length);
} catch (UnsupportedEncodingException e) {
throw new TException("UTF-8 not supported!");
}
}
/**
* 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 "";
}
try {
if (trans_.getBytesRemainingInBuffer() >= length) {
String str = new String(trans_.getBuffer(), trans_.getBufferPosition(), length, "UTF-8");
trans_.consumeBuffer(length);
return str;
} else {
return new String(readBinary(length), "UTF-8");
}
} catch (UnsupportedEncodingException e) {
throw new TException("UTF-8 not supported!");
}
}
/**
* 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];
}
}

View file

@ -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.protocol;
/**
* Helper class that encapsulates field metadata.
*
*/
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 + ((name == null) ? 0 : name.hashCode());
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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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_;
}
}

View file

@ -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);
}

View file

@ -0,0 +1,220 @@
/*
* 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:
break;
}
}
/**
* 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 &lt; 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;
}
}

View file

@ -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;
}

View file

@ -0,0 +1,448 @@
/*
* 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.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);
}
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);
}
public void writeMessageEnd() throws TException {
popWriteContext();
trans_.write(RBRACKET);
}
public void writeStructBegin(TStruct struct) throws TException {
writeContext_.write();
trans_.write(LBRACE);
pushWriteContext(new StructContext());
}
public void writeStructEnd() throws TException {
popWriteContext();
trans_.write(RBRACE);
}
public void writeFieldBegin(TField field) throws TException {
// Note that extra type information is omitted in JSON!
writeString(field.name);
}
public void writeFieldEnd() {}
public void writeFieldStop() {}
public void writeMapBegin(TMap map) throws TException {
assertContextIsNotMapKey(MAP);
writeContext_.write();
trans_.write(LBRACE);
pushWriteContext(new MapContext());
// No metadata!
}
public void writeMapEnd() throws TException {
popWriteContext();
trans_.write(RBRACE);
}
public void writeListBegin(TList list) throws TException {
assertContextIsNotMapKey(LIST);
writeContext_.write();
trans_.write(LBRACKET);
pushWriteContext(new ListContext());
// No metadata!
}
public void writeListEnd() throws TException {
popWriteContext();
trans_.write(RBRACKET);
}
public void writeSetBegin(TSet set) throws TException {
assertContextIsNotMapKey(SET);
writeContext_.write();
trans_.write(LBRACKET);
pushWriteContext(new ListContext());
// No metadata!
}
public void writeSetEnd() throws TException {
popWriteContext();
trans_.write(RBRACKET);
}
public void writeBool(boolean b) throws TException {
writeByte(b ? (byte)1 : (byte)0);
}
public void writeByte(byte b) throws TException {
writeI32(b);
}
public void writeI16(short i16) throws TException {
writeI32(i16);
}
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 {
try {
byte[] b = s.getBytes("UTF-8");
trans_.write(b);
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
public void writeI64(long i64) throws TException {
if(writeContext_.isMapKey()) {
writeString(Long.toString(i64));
} else {
writeContext_.write();
_writeStringData(Long.toString(i64));
}
}
public void writeDouble(double dub) throws TException {
if(writeContext_.isMapKey()) {
writeString(Double.toString(dub));
} else {
writeContext_.write();
_writeStringData(Double.toString(dub));
}
}
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());
}
public void writeBinary(ByteBuffer bin) throws TException {
try {
// TODO(mcslee): Fix this
writeString(new String(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset(), "UTF-8"));
} catch (UnsupportedEncodingException uex) {
throw new TException("JVM DOES NOT SUPPORT UTF-8");
}
}
/**
* Reading methods.
*/
public TMessage readMessageBegin() throws TException {
// TODO(mcslee): implement
return EMPTY_MESSAGE;
}
public void readMessageEnd() {}
public TStruct readStructBegin() {
// TODO(mcslee): implement
return ANONYMOUS_STRUCT;
}
public void readStructEnd() {}
public TField readFieldBegin() throws TException {
// TODO(mcslee): implement
return ANONYMOUS_FIELD;
}
public void readFieldEnd() {}
public TMap readMapBegin() throws TException {
// TODO(mcslee): implement
return EMPTY_MAP;
}
public void readMapEnd() {}
public TList readListBegin() throws TException {
// TODO(mcslee): implement
return EMPTY_LIST;
}
public void readListEnd() {}
public TSet readSetBegin() throws TException {
// TODO(mcslee): implement
return EMPTY_SET;
}
public void readSetEnd() {}
public boolean readBool() throws TException {
return (readByte() == 1);
}
public byte readByte() throws TException {
// TODO(mcslee): implement
return 0;
}
public short readI16() throws TException {
// TODO(mcslee): implement
return 0;
}
public int readI32() throws TException {
// TODO(mcslee): implement
return 0;
}
public long readI64() throws TException {
// TODO(mcslee): implement
return 0;
}
public double readDouble() throws TException {
// TODO(mcslee): implement
return 0;
}
public String readString() throws TException {
// TODO(mcslee): implement
return "";
}
public String readStringBody(int size) throws TException {
// TODO(mcslee): implement
return "";
}
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);
}
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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> {
}

View file

@ -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> {
}

View file

@ -0,0 +1,612 @@
/*
* 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.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;
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;
/**
* 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 = Long.MAX_VALUE;
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 final 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() {
if (state_ == FrameBufferState.AWAITING_REGISTER_WRITE) {
// set the OP_WRITE interest
selectionKey_.interestOps(SelectionKey.OP_WRITE);
state_ = FrameBufferState.WRITING;
} else if (state_ == FrameBufferState.AWAITING_REGISTER_READ) {
prepareRead();
} else if (state_ == FrameBufferState.AWAITING_CLOSE) {
close();
selectionKey_.cancel();
} else {
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();
}
}
}

View file

@ -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();
}
}

View file

@ -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 {}

View file

@ -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
*/
protected abstract TProcessor getProcessor();
/**
* Returns the appropriate in {@link TProtocolFactory}. This will be called
* <b>once</b> just after the {@link #init()} method
*
* @return
*/
protected abstract TProtocolFactory getInProtocolFactory();
/**
* Returns the appropriate out {@link TProtocolFactory}. This will be called
* <b>once</b> just after the {@link #init()} method
*
* @return
*/
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);
}
}

View file

@ -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.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 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);
}
}

View file

@ -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.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) {
// for now, just silently ignore. technically this means we'll have less of
// a graceful shutdown as a result.
}
}
/**
* 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);
tte.printStackTrace();
if (clientKey != null) cleanupSelectionKey(clientKey);
if (client != null) client.close();
}
}
} // SelectAcceptThread
}

View file

@ -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 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;
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -0,0 +1,117 @@
/*
* 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);
}
if(!processor.process(inputProtocol, outputProtocol)) {
break;
}
}
}
} 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();
}
}

View file

@ -0,0 +1,314 @@
/*
* 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.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.TSaslTransportException;
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 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,
TimeUnit.SECONDS,
executorQueue);
}
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();
}
stopped_ = false;
setServing(true);
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);
}
}
}
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;
}
}
setServing(false);
}
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_ || !processor.process(inputProtocol, outputProtocol)) {
break;
}
}
} catch (TSaslTransportException ttx) {
// Something thats not SASL was in the stream, continue silently
} catch (TTransportException ttx) {
// Assume the client died and continue silently
} catch (TException tx) {
LOGGER.error("Thrift error occurred during processing of message.", tx);
} catch (Exception x) {
LOGGER.error("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();
}
}
}
}
}

View file

@ -0,0 +1,668 @@
/*
* 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.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;
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;
/**
* 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;
/**
* 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 {
// 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 (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);
}
}
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();
}
}
}

View file

@ -0,0 +1,52 @@
/*
* 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;
/**
* 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.
*/
public class AutoExpandingBuffer {
private byte[] array;
private final double growthCoefficient;
public AutoExpandingBuffer(int initialCapacity, double growthCoefficient) {
if (growthCoefficient < 1.0) {
throw new IllegalArgumentException("Growth coefficient must be >= 1.0");
}
array = new byte[initialCapacity];
this.growthCoefficient = growthCoefficient;
}
public void resizeIfNecessary(int size) {
if (array.length < size) {
byte[] newBuf = new byte[(int)(size * growthCoefficient)];
System.arraycopy(array, 0, newBuf, 0, array.length);
array = newBuf;
}
}
public byte[] array() {
return array;
}
}

View file

@ -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, double overgrowthCoefficient) {
this.buf = new AutoExpandingBuffer(initialCapacity, overgrowthCoefficient);
}
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;
}
}

View file

@ -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.transport;
/**
* TTransport for writing to an AutoExpandingBuffer.
*/
public final class AutoExpandingBufferWriteTransport extends TTransport {
private final AutoExpandingBuffer buf;
private int pos;
public AutoExpandingBufferWriteTransport(int initialCapacity, double growthCoefficient) {
this.buf = new AutoExpandingBuffer(initialCapacity, growthCoefficient);
this.pos = 0;
}
@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;
}
public int getPos() {
return pos;
}
public void reset() {
pos = 0;
}
}

View file

@ -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, len);
} 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;
}
}

View file

@ -0,0 +1,191 @@
/*
* 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 final AutoExpandingBufferReadTransport readBuffer;
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;
writeBuffer = new AutoExpandingBufferWriteTransport(initialBufferCapacity, 1.5);
readBuffer = new AutoExpandingBufferReadTransport(initialBufferCapacity, 1.5);
}
@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);
}
@Override
public void flush() throws TTransportException {
int length = writeBuffer.getPos();
TFramedTransport.encodeFrameSize(length, i32buf);
underlying.write(i32buf, 0, 4);
underlying.write(writeBuffer.getBuf().array(), 0, length);
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();
}
}

View file

@ -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());
}
}

View file

@ -0,0 +1,621 @@
/*
* 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;
/**
* 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 {
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) {
System.err.println("createInputStream: "+iox.getMessage());
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(), 8192);
} 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) {
System.err.println("WARNING: Error closing input file: " +
iox.getMessage());
}
inputFile_ = null;
}
if (outputStream_ != null) {
try {
outputStream_.close();
} catch (IOException iox) {
System.err.println("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) {
System.err.println("createInputStream: "+iox.getMessage());
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) {
System.err.println("Cannot parse " + args[1]);
printUsage();
}
}
TFileTransport t = new TFileTransport(args[0], true);
t.open();
System.out.println("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);
System.out.println("Reading chunk "+cnum);
t.seekToChunk(cnum);
for(int i=0; i<4096; i++) {
t.read(buf, 0, 4096);
}
}
}
private static void printUsage() {
System.err.println("Usage: TFileTransport <filename> [num_chunks]");
System.err.println(" (Opens and reads num_chunks chunks from file randomly)");
System.exit(1);
}
}

View file

@ -0,0 +1,178 @@
/*
* 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 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_);
}
}
/**
* Constructor wraps around another transport
*/
public TFramedTransport(TTransport transport, int maxLength) {
transport_ = transport;
maxLength_ = maxLength;
}
public TFramedTransport(TTransport transport) {
transport_ = transport;
maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
}
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);
}
@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);
}
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();
writeBuffer_.reset();
encodeFrameSize(len, i32buf);
transport_.write(i32buf, 0, 4);
transport_.write(buf, 0, len);
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));
}
}

View file

@ -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.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);
}
}
}
}
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);
}
}
}

View file

@ -0,0 +1,164 @@
/*
* 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;
}
/**
* 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) {
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);
}
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);
}
}
}

View file

@ -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.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);
}
@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 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() {
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();
}
}

View file

@ -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;
}
}

View file

@ -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() + ".");
}
}
public void listen() throws TTransportException {
// Make sure not to block on accept
if (serverSocket_ != null) {
try {
serverSocket_.setSoTimeout(0);
} catch (SocketException sx) {
sx.printStackTrace();
}
}
}
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();
}
}

View file

@ -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);
}

View file

@ -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();
}
}

Some files were not shown because too many files have changed in this diff Show more