├── library ├── project │ ├── build.properties │ └── plugins.sbt ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── meituan │ │ │ └── firefly │ │ │ ├── Processor.java │ │ │ ├── annotations │ │ │ ├── Union.java │ │ │ ├── Func.java │ │ │ └── Field.java │ │ │ ├── Interceptor.java │ │ │ ├── util │ │ │ └── Maps.java │ │ │ ├── adapters │ │ │ ├── EnumTypeAdapterFactory.java │ │ │ ├── SetTypeAdapterFactory.java │ │ │ ├── ListTypeAdapterFactory.java │ │ │ ├── MapTypeAdapterFactory.java │ │ │ └── StructTypeAdapterFactory.java │ │ │ ├── OkhttpTransport.java │ │ │ ├── TypeAdapter.java │ │ │ ├── Types.java │ │ │ ├── FunctionCall.java │ │ │ └── Thrift.java │ └── test │ │ ├── java │ │ └── com │ │ │ └── meituan │ │ │ └── firefly │ │ │ ├── testfirefly │ │ │ ├── OrderedStruct.java │ │ │ ├── TestException.java │ │ │ ├── UnorderedStruct.java │ │ │ ├── MixStruct.java │ │ │ ├── UnionB.java │ │ │ ├── OrderEnum.java │ │ │ ├── TestService.java │ │ │ └── ComplicatedStruct.java │ │ │ ├── rx_testfirefly │ │ │ ├── OrderedStruct.java │ │ │ ├── TestException.java │ │ │ ├── UnorderedStruct.java │ │ │ ├── MixStruct.java │ │ │ ├── UnionB.java │ │ │ ├── OrderEnum.java │ │ │ ├── TestService.java │ │ │ └── ComplicatedStruct.java │ │ │ ├── FlushableMemoryBuffer.java │ │ │ ├── testthrift │ │ │ ├── OrderEnum.java │ │ │ ├── OrderedStruct.java │ │ │ ├── UnorderedStruct.java │ │ │ └── TestException.java │ │ │ ├── NotifyCheck.java │ │ │ ├── SerializationTest.java │ │ │ ├── ThriftTest.java │ │ │ └── adapters │ │ │ └── StructTypeAdapterTest.java │ │ └── idl │ │ └── test.thrift └── build.sbt ├── generator ├── project │ ├── build.properties │ ├── assembly.sbt │ ├── META-INF │ │ └── MANIFEST.MF │ └── plugins.sbt ├── src │ └── main │ │ ├── resources │ │ ├── field.mustache │ │ ├── const.mustache │ │ ├── service.mustache │ │ ├── function.mustache │ │ ├── enum.mustache │ │ └── struct.mustache │ │ └── scala-2.11 │ │ └── com │ │ └── meituan │ │ └── firefly │ │ ├── node │ │ ├── Function.scala │ │ ├── Document.scala │ │ ├── Field.scala │ │ ├── ConstValue.scala │ │ ├── Identifier.scala │ │ ├── Type.scala │ │ ├── Definition.scala │ │ └── Header.scala │ │ ├── ConvertException.scala │ │ ├── Compiler.scala │ │ ├── Main.scala │ │ ├── ParserException.scala │ │ └── ThriftParser.scala └── build.sbt ├── gradle_plugin ├── settings.gradle ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── com.meituan.firefly.properties │ │ └── groovy │ │ └── com │ │ └── meituan │ │ ├── FireflyArgs.groovy │ │ ├── FireflyPlugin.groovy │ │ └── FireflyTask.groovy ├── local.properties └── build.gradle ├── lib-android ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── meituan │ │ │ └── firefly │ │ │ └── android │ │ │ └── StructBase.java │ ├── test │ │ ├── idl │ │ │ └── test.thrift │ │ └── java │ │ │ └── com │ │ │ └── meituan │ │ │ └── firefly │ │ │ └── android │ │ │ ├── test │ │ │ ├── TestEnum.java │ │ │ └── TestStruct.java │ │ │ └── ParcelableTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── meituan │ │ └── firefly │ │ └── lib_android │ │ └── ApplicationTest.java ├── gradle.properties ├── gradlew.bat ├── build.gradle └── gradlew ├── .gitignore ├── CHANGELOG.md ├── README.md └── LICENSE.txt /library/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.8 -------------------------------------------------------------------------------- /generator/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.8 -------------------------------------------------------------------------------- /gradle_plugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'gradle-plugin' 2 | -------------------------------------------------------------------------------- /lib-android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'lib-android' 2 | -------------------------------------------------------------------------------- /generator/project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0") -------------------------------------------------------------------------------- /generator/project/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: com.meituan.firefly.Main 3 | 4 | -------------------------------------------------------------------------------- /generator/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn 2 | 3 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") -------------------------------------------------------------------------------- /gradle_plugin/src/main/resources/META-INF/gradle-plugins/com.meituan.firefly.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.meituan.FireflyPlugin 2 | -------------------------------------------------------------------------------- /lib-android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meituan/Firefly/HEAD/lib-android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lib-android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /library/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn 2 | 3 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 4 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.0") 5 | -------------------------------------------------------------------------------- /generator/src/main/resources/field.mustache: -------------------------------------------------------------------------------- 1 | {{&doc}} 2 | @Field(id = {{&id}}, required = {{required}}, name = "{{&name}}") 3 | public {{&fieldType}} {{&name}}{{#value}} = {{/value}}{{&value}}; -------------------------------------------------------------------------------- /lib-android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Function.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | case class Function(functionType: Type, name: SimpleId, params: Seq[Field], throws: Option[Seq[Field]], comment: Option[String]) 4 | -------------------------------------------------------------------------------- /lib-android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /generator/src/main/resources/const.mustache: -------------------------------------------------------------------------------- 1 | package {{package}}; 2 | 3 | import com.meituan.firefly.annotations.*; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | public interface Consts { 9 | {{#consts}} 10 | {{&doc}} 11 | {{&fieldType}} {{&name}} = {{&value}}; 12 | {{/consts}} 13 | } -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/Processor.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import org.apache.thrift.protocol.TProtocol; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | public interface Processor { 8 | Object process(Method method, Object[] args, TProtocol protocol, int seqId) throws Throwable; 9 | } 10 | -------------------------------------------------------------------------------- /lib-android/src/test/idl/test.thrift: -------------------------------------------------------------------------------- 1 | namespace java com.meituan.firefly.android.test 2 | 3 | enum TestEnum { 4 | A =1111111111, B = 2; 5 | } 6 | 7 | struct TestStruct { 8 | i32 a = 123456789; 9 | bool b; 10 | i64 c; 11 | TestEnum e = TestEnum.B; 12 | binary bin; 13 | string str = 'abcde'; 14 | string ss = "fghij"; 15 | } 16 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Document.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | /** 4 | * Every Thrift document contains 0 or more headers followed by 0 or more definitions. 5 | *
 6 |  * Document        ::=  Header* Definition*
 7 |  * 
8 | * 9 | */ 10 | case class Document(headers: Seq[Header], defs: Seq[Definition]) 11 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/annotations/Union.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface Union { 11 | } 12 | -------------------------------------------------------------------------------- /generator/src/main/resources/service.mustache: -------------------------------------------------------------------------------- 1 | package {{package}}; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import org.apache.thrift.TException; 7 | import rx.Observable; 8 | import com.meituan.firefly.annotations.*; 9 | 10 | {{&doc}} 11 | public interface {{&name}} {{#parent}}extends {{&parent}} {{/parent}}{ 12 | {{#functions}} 13 | {{> function}} 14 | {{/functions}} 15 | } 16 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/OrderedStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class OrderedStruct implements Serializable { 11 | 12 | @Field(id = 1, required = true, name = "id") 13 | public Integer id; 14 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/OrderedStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class OrderedStruct implements Serializable { 11 | 12 | @Field(id = 1, required = true, name = "id") 13 | public Integer id; 14 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/TestException.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class TestException extends Exception { 11 | 12 | @Field(id = 1, required = false, name = "message") 13 | public String message; 14 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/UnorderedStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class UnorderedStruct implements Serializable { 11 | 12 | @Field(id = 1, required = true, name = "id") 13 | public Integer id; 14 | } -------------------------------------------------------------------------------- /generator/src/main/resources/function.mustache: -------------------------------------------------------------------------------- 1 | {{&doc}} 2 | @Func(oneway = {{oneway}}, value = { {{#exceptions}}{{^first}}, {{/first}}@Field(id = {{id}}, required = {{required}}, name = "{{&name}}"){{/exceptions}} }) 3 | public {{&funcType}} {{&name}} ({{#params}}{{^first}}, {{/first}}@Field(id = {{id}}, required = {{required}}, name = "{{&name}}") {{&fieldType}} {{&name}}{{/params}}) throws {{#exceptions}}{{&fieldType}}, {{/exceptions}} TException; -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/TestException.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class TestException extends Exception { 11 | 12 | @Field(id = 1, required = false, name = "message") 13 | public String message; 14 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/UnorderedStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class UnorderedStruct implements Serializable { 11 | 12 | @Field(id = 1, required = true, name = "id") 13 | public Integer id; 14 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/FlushableMemoryBuffer.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import org.apache.thrift.transport.TMemoryBuffer; 4 | import org.apache.thrift.transport.TTransportException; 5 | 6 | class FlushableMemoryBuffer extends TMemoryBuffer { 7 | public FlushableMemoryBuffer(int size) { 8 | super(size); 9 | } 10 | 11 | @Override 12 | public void flush() throws TTransportException { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Field.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | case class Field(id: Option[Int], requiredness: Option[Requiredness], fieldType: Type, identifier: SimpleId, value: Option[ConstValue], comment: Option[String]) 4 | 5 | sealed abstract class Requiredness 6 | 7 | object Requiredness { 8 | 9 | case object Required extends Requiredness 10 | 11 | case object Optional extends Requiredness 12 | 13 | } 14 | -------------------------------------------------------------------------------- /lib-android/src/androidTest/java/com/meituan/firefly/lib_android/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.lib_android; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/annotations/Func.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.annotations; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.METHOD; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | @Target(METHOD) 11 | public @interface Func { 12 | boolean oneway() default false; 13 | 14 | Field[] value() default {}; 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | # Please see https://github.com/github/gitignore for some nice presets 4 | 5 | # Output directories 6 | build/ 7 | out/ 8 | target/ 9 | 10 | # Mac OS X files 11 | .DS_Store 12 | 13 | # IntelliJ IDEA files 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # Eclipse files 20 | .settings 21 | .classpath 22 | .project 23 | 24 | # Gradle 25 | .gradle/ 26 | 27 | # Log directory 28 | logs/ 29 | 30 | # vim files 31 | .*.sw[a-z] 32 | *.un~ 33 | 34 | # Java class files 35 | *.class 36 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/annotations/Field.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.annotations; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.*; 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Target({FIELD, METHOD, PARAMETER}) 10 | @Retention(RUNTIME) 11 | public @interface Field { 12 | boolean required() default true; 13 | 14 | short id(); 15 | 16 | String name(); 17 | } 18 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/MixStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class MixStruct implements Serializable { 11 | 12 | @Field(id = 1, required = true, name = "id") 13 | public Integer id; 14 | 15 | @Field(id = 2, required = true, name = "uid") 16 | public Integer uid; 17 | } -------------------------------------------------------------------------------- /gradle_plugin/local.properties: -------------------------------------------------------------------------------- 1 | ## This file is automatically generated by Android Studio. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must *NOT* be checked into Version Control Systems, 5 | # as it contains information specific to your local configuration. 6 | # 7 | # Location of the SDK. This is only used by Gradle. 8 | # For customization when using a Version Control System, please read the 9 | # header note. 10 | #Mon Nov 30 17:13:50 CST 2015 11 | sdk.dir=/Users/ponyets/dev/android-sdk 12 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/MixStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class MixStruct implements Serializable { 11 | 12 | @Field(id = 1, required = true, name = "id") 13 | public Integer id; 14 | 15 | @Field(id = 2, required = true, name = "uid") 16 | public Integer uid; 17 | } -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/Interceptor.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import org.apache.thrift.protocol.TProtocol; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | /** 8 | * Observes, modifies, and potentially short-circuits method calls to the service interface's dynamic proxy. 9 | * May be used to log calls, modify method arguments, or implement cache mechanism. 10 | */ 11 | public interface Interceptor { 12 | Object intercept(Method method, Object[] args, TProtocol protocol, int seqId, Processor processor) throws Throwable; 13 | } 14 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/ConstValue.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | sealed abstract class ConstValue 4 | 5 | case class Literal(value: String) extends ConstValue 6 | 7 | case class IntConstant(value: Long) extends ConstValue 8 | 9 | case class BoolConstant(value: Boolean) extends ConstValue 10 | 11 | case class DoubleConstant(value: Double) extends ConstValue 12 | 13 | case class IdConstant(id: Identifier) extends ConstValue 14 | 15 | case class ConstList(elems: Seq[ConstValue]) extends ConstValue 16 | 17 | case class ConstMap(elems: Seq[(ConstValue, ConstValue)]) extends ConstValue 18 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/UnionB.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | @Union 11 | public class UnionB implements Serializable { 12 | 13 | @Field(id = 1, required = false, name = "os") 14 | public OrderedStruct os; 15 | 16 | @Field(id = 2, required = false, name = "uos") 17 | public UnorderedStruct uos; 18 | 19 | @Field(id = 3, required = false, name = "mos") 20 | public MixStruct mos; 21 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/UnionB.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | @Union 11 | public class UnionB implements Serializable { 12 | 13 | @Field(id = 1, required = false, name = "os") 14 | public OrderedStruct os; 15 | 16 | @Field(id = 2, required = false, name = "uos") 17 | public UnorderedStruct uos; 18 | 19 | @Field(id = 3, required = false, name = "mos") 20 | public MixStruct mos; 21 | } -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/util/Maps.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.util; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public final class Maps { 8 | private Maps() { 9 | } 10 | 11 | public static Map asMap(Map.Entry... kvs) { 12 | if (kvs == null) { 13 | return Collections.emptyMap(); 14 | } 15 | Map map = new HashMap(kvs.length); 16 | for (Map.Entry kv : kvs) { 17 | map.put(kv.getKey(), kv.getValue()); 18 | } 19 | return Collections.unmodifiableMap(map); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib-android/src/test/java/com/meituan/firefly/android/test/TestEnum.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.android.test; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | 7 | public enum TestEnum { 8 | B(2),A(1111111111); 9 | private final static Map map = new HashMap(); 10 | static { 11 | map.put(2, B); 12 | map.put(1111111111, A); 13 | } 14 | private final int value; 15 | private TestEnum (int value) { 16 | this.value = value; 17 | } 18 | 19 | public int getValue(){ return value; } 20 | 21 | public static TestEnum findByValue(int value) { return map.get(value); } 22 | } -------------------------------------------------------------------------------- /gradle_plugin/src/main/groovy/com/meituan/FireflyArgs.groovy: -------------------------------------------------------------------------------- 1 | package com.meituan 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.tasks.Input 5 | import org.gradle.api.tasks.InputDirectory 6 | import org.gradle.api.tasks.OutputDirectory 7 | 8 | /** 9 | * Created by zhangmeng on 15/11/21. 10 | */ 11 | class FireflyArgs { 12 | @InputDirectory 13 | File inputDir 14 | @OutputDirectory 15 | File outputDir 16 | @Input 17 | boolean rxStyle = false 18 | @Input 19 | boolean android = false 20 | 21 | FireflyArgs(Project project){ 22 | inputDir = new File("${project.projectDir}/src/main/idl") 23 | outputDir = new File("${project.buildDir}/generated/source/firefly") 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/OrderEnum.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.util.List; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | 9 | public enum OrderEnum { 10 | Order(1),UnOrder(2),Mix(3); 11 | private final static Map map = new HashMap(); 12 | static { 13 | map.put(1, Order); 14 | map.put(2, UnOrder); 15 | map.put(3, Mix); 16 | } 17 | private final int value; 18 | private OrderEnum (int value) { 19 | this.value = value; 20 | } 21 | 22 | public int getValue(){ return value; } 23 | 24 | public static OrderEnum findByValue(int value) { return map.get(value); } 25 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/OrderEnum.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.util.List; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | 9 | public enum OrderEnum { 10 | Order(1),UnOrder(2),Mix(3); 11 | private final static Map map = new HashMap(); 12 | static { 13 | map.put(1, Order); 14 | map.put(2, UnOrder); 15 | map.put(3, Mix); 16 | } 17 | private final int value; 18 | private OrderEnum (int value) { 19 | this.value = value; 20 | } 21 | 22 | public int getValue(){ return value; } 23 | 24 | public static OrderEnum findByValue(int value) { return map.get(value); } 25 | } -------------------------------------------------------------------------------- /generator/build.sbt: -------------------------------------------------------------------------------- 1 | organization := "com.meituan.firefly" 2 | 3 | name := "generator" 4 | 5 | version := "0.2.4" 6 | 7 | scalaVersion := "2.11.6" 8 | 9 | libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4" 10 | 11 | libraryDependencies += "com.github.scopt" %% "scopt" % "3.3.0" 12 | 13 | libraryDependencies += "org.scalatra.scalate" %% "scalate-core" % "1.7.0" 14 | 15 | libraryDependencies += "org.slf4j" % "slf4j-simple" % "1.7.12" 16 | 17 | publishMavenStyle := true 18 | 19 | licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0")) 20 | 21 | bintrayPackage := "com.meituan.firefly:generator_2.11" 22 | 23 | bintrayPackageLabels := Seq("thrift", "android") 24 | 25 | javacOptions in (Compile, compile) ++= Seq("-source", "1.7", "-target", "1.7") 26 | -------------------------------------------------------------------------------- /generator/src/main/resources/enum.mustache: -------------------------------------------------------------------------------- 1 | package {{package}}; 2 | 3 | import java.util.List; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | {{&doc}} 9 | public enum {{name}} { 10 | {{#elems}} 11 | {{name}}({{id}}){{#last}};{{/last}}{{^last}},{{/last}} 12 | {{/elems}} 13 | {{emptyLine}} 14 | private final static Map map = new HashMap(); 15 | static { 16 | {{#elems}} 17 | map.put({{id}}, {{name}}); 18 | {{/elems}} 19 | } 20 | private final int value; 21 | private {{name}} (int value) { 22 | this.value = value; 23 | } 24 | 25 | public int getValue(){ return value; } 26 | 27 | public static {{name}} findByValue(int value) { return map.get(value); } 28 | } -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Identifier.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | sealed abstract class Identifier { 4 | def fullName: String 5 | } 6 | 7 | case class SimpleId(name: String) extends Identifier { 8 | override def fullName: String = name 9 | } 10 | 11 | case class QualifiedId(ids: Seq[String]) extends Identifier { 12 | assert(ids.size >= 2) 13 | assert(!ids.exists(_.isEmpty)) 14 | 15 | def qualifier = ids.dropRight(1).mkString(".") 16 | 17 | def name = ids.last 18 | 19 | override def fullName: String = ids.mkString(".") 20 | } 21 | 22 | object Identifier { 23 | def apply(s: String): Identifier = { 24 | assert(!s.isEmpty) 25 | val ids = s.split("\\.") 26 | if (ids.size == 1) SimpleId(ids.head) 27 | else QualifiedId(ids) 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/ConvertException.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly 2 | 3 | class ConvertException(message: String) extends Exception(message) 4 | 5 | class TypeNotFoundException(struct: String)(implicit exceptionContext: String) extends ConvertException(s"$exceptionContext: Struct $struct Not Found") 6 | 7 | class IncludeNotFoundException(file: String)(implicit exceptionContext: String) extends ConvertException(s"$exceptionContext: Include $file Not Found") 8 | 9 | class ValueTypeNotMatchException(expect: String, actual: String)(implicit exceptionContext: String) extends ConvertException(s"$exceptionContext: Type $expect expected but got $actual type value") 10 | 11 | class ServiceNotFoundException(service: String)(implicit exceptionContext: String) extends ConvertException(s"$exceptionContext: Struct $service Not Found") 12 | -------------------------------------------------------------------------------- /generator/src/main/resources/struct.mustache: -------------------------------------------------------------------------------- 1 | package {{package}}; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | {{^isException}}{{#android}} 8 | import android.os.Parcel; 9 | import android.os.Parcelable; 10 | import com.meituan.firefly.android.StructBase; 11 | {{/android}}{{/isException}} 12 | import com.meituan.firefly.annotations.*; 13 | 14 | {{&doc}} 15 | {{#isUnion}} 16 | @Union 17 | {{/isUnion}} 18 | public class {{&name}} {{#isException}} extends Exception {{/isException}} {{^isException}}{{#android}} extends StructBase {{/android}}{{^android}} implements Serializable {{/android}}{{/isException}}{ 19 | {{^isException}}{{#android}} 20 | public static final Parcelable.Creator CREATOR = new StructBase.Creator({{&name}}.class); 21 | {{/android}}{{/isException}} 22 | {{#fields}} 23 | {{> field}} 24 | {{/fields}} 25 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ------- 4 | 5 | ## 0.2.4 6 | * Bugfix: Misuse of project.projectDir in gradle plugin 7 | 8 | ## 0.2.3 9 | * Bugfix: If enum value is > 1k, comma is added in the integer in the generated Java file 10 | * Bugfix: Single quotation mark is not accepted during Java files generation 11 | * Gradle plugin use projectDir instead relative path 12 | 13 | ## 0.2.2 14 | * Provide a gradle plugin that generates code from thrift files automatically. 15 | * Support Serializable. 16 | * Support Android's Parcelable. 17 | * Bugfix: binary field type was generated to byte array but processed as ByteBuffer. 18 | * Bugfix: can not specify a default value to enum type field. 19 | * Bugfix: crash when call none argument method on Android 5.0. 20 | 21 | ## 0.2.0 22 | 23 | * RxJava support. 24 | * Better exception handle to method with `oneway void` or `void` return type. 25 | 26 | ## 0.1.4 27 | 28 | Initial release. 29 | -------------------------------------------------------------------------------- /lib-android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Type.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | 4 | sealed abstract class Type 5 | 6 | case object OnewayVoid extends Type 7 | 8 | case object Void extends Type 9 | 10 | sealed abstract class BaseType extends Type 11 | 12 | case object TBool extends BaseType 13 | 14 | case object TByte extends BaseType 15 | 16 | case object TI16 extends BaseType 17 | 18 | case object TI32 extends BaseType 19 | 20 | case object TI64 extends BaseType 21 | 22 | case object TDouble extends BaseType 23 | 24 | case object TString extends BaseType 25 | 26 | case object TBinary extends BaseType 27 | 28 | sealed abstract class ContainerType extends Type 29 | 30 | case class MapType(keyType: Type, valueType: Type) extends ContainerType 31 | 32 | case class SetType(typeParam: Type) extends ContainerType 33 | 34 | case class ListType(typeParam: Type) extends ContainerType 35 | 36 | case class IdentifierType(id: Identifier) extends Type 37 | 38 | -------------------------------------------------------------------------------- /library/build.sbt: -------------------------------------------------------------------------------- 1 | organization := "com.meituan.firefly" 2 | 3 | name := "library" 4 | 5 | version := "0.2.5" 6 | 7 | scalaVersion := "2.11.6" 8 | 9 | libraryDependencies += "org.apache.thrift" % "libthrift" % "0.9.3" 10 | 11 | libraryDependencies += "com.squareup.okhttp" % "okhttp" % "2.4.0" 12 | 13 | libraryDependencies += "io.reactivex" % "rxjava" % "1.0.15" 14 | 15 | libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test 16 | 17 | libraryDependencies += "org.assertj" % "assertj-core" % "2.1.0" % Test 18 | 19 | libraryDependencies += "org.slf4j" % "slf4j-simple" % "1.7.12" % Test 20 | 21 | autoScalaLibrary := false 22 | 23 | publishMavenStyle := true 24 | 25 | crossPaths := false 26 | 27 | javacOptions in (Compile, compile) ++= Seq("-source", "1.7", "-target", "1.7") 28 | 29 | licenses += ("Apache-2.0", url("http://www.apache.org/licenses/LICENSE-2.0")) 30 | 31 | bintrayPackage := "com.meituan.firefly:library" 32 | 33 | bintrayPackageLabels := Seq("thrift", "android") 34 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/Compiler.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly 2 | 3 | import java.io.File 4 | 5 | /** 6 | * A compiler stores configurations of command line. 7 | * It processes thrift files one by one, parse thrift file into Document than generate code from Document. 8 | * @param thriftFiles target thrift files to compile 9 | * @param output output dir of generated code, specified by --output argument 10 | */ 11 | case class Compiler(thriftFiles: List[File] = List(), output: File = new File("gen"), rxStyle: Boolean = false, androidSupport: Boolean = false) { 12 | 13 | /** 14 | * Generates java code from thrift files 15 | */ 16 | def run(): Unit = { 17 | val generator = new Generator(output = output, rxStyle = rxStyle, androidSupport = androidSupport) 18 | thriftFiles.foreach { 19 | file => 20 | val document = new ThriftParser(file.getParentFile).parseFile(file) 21 | generator(document, file.getName) 22 | } 23 | } 24 | } 25 | 26 | object Compiler { 27 | } 28 | -------------------------------------------------------------------------------- /lib-android/src/test/java/com/meituan/firefly/android/test/TestStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.android.test; 2 | 3 | import android.os.Parcelable; 4 | 5 | import com.meituan.firefly.android.StructBase; 6 | import com.meituan.firefly.annotations.*; 7 | 8 | 9 | public class TestStruct extends StructBase { 10 | public static final Parcelable.Creator CREATOR = new Creator(TestStruct.class); 11 | 12 | @Field(id = -1, required = false, name = "a") 13 | public Integer a= 123456789; 14 | 15 | @Field(id = -2, required = false, name = "b") 16 | public Boolean b; 17 | 18 | @Field(id = -3, required = false, name = "c") 19 | public Long c; 20 | 21 | @Field(id = -4, required = false, name = "e") 22 | public TestEnum e= TestEnum.B; 23 | 24 | @Field(id = -5, required = false, name = "bin") 25 | public byte[] bin; 26 | 27 | @Field(id = -6, required = false, name = "str") 28 | public String str= "abcde"; 29 | 30 | @Field(id = -7, required = false, name = "ss") 31 | public String ss= "fghij"; 32 | } -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Definition.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | sealed abstract class Definition 4 | 5 | case class Const(fieldType: Type, name: SimpleId, value: ConstValue, comment: Option[String]) extends Definition 6 | 7 | case class Typedef(definitionType: Type, name: SimpleId, comment: Option[String]) extends Definition 8 | 9 | case class Enum(name: SimpleId, elems: Seq[(SimpleId, Int)], comment: Option[String]) extends Definition 10 | 11 | sealed abstract class StructLike extends Definition { 12 | val name: SimpleId 13 | val fields: Seq[Field] 14 | val comment: Option[String] 15 | } 16 | 17 | case class Struct(name: SimpleId, fields: Seq[Field], comment: Option[String]) extends StructLike 18 | 19 | case class Union(name: SimpleId, fields: Seq[Field], comment: Option[String]) extends StructLike 20 | 21 | case class ExceptionDef(name: SimpleId, fields: Seq[Field], comment: Option[String]) extends StructLike 22 | 23 | case class Service(name: SimpleId, parent: Option[Identifier], functions: Seq[Function], comment: Option[String]) extends Definition -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/Main.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly 2 | 3 | import java.io.File 4 | 5 | import scopt.OptionParser 6 | 7 | /** 8 | * Application's entrance 9 | */ 10 | object Main { 11 | 12 | def main(args: Array[String]): Unit = { 13 | parser.parse(args, Compiler()).foreach(_.run()) 14 | } 15 | 16 | /** 17 | * a parser that parse command line arguments 18 | */ 19 | val parser = new OptionParser[Compiler]("Firefly") { 20 | help("help") text "prints this usage text." 21 | arg[File]("...") unbounded() action { (file, c) => 22 | c.copy(thriftFiles = file :: c.thriftFiles) 23 | } text "thrift files." 24 | opt[File]("output") valueName ("") action { (file, c) => 25 | assert(file.isDirectory) 26 | c.copy(output = file) 27 | } text "gen code output dir." 28 | opt[Unit]("rx") action { 29 | (sequence, c) => { 30 | c.copy(rxStyle = true) 31 | } 32 | } text "gen code with Rx smell." 33 | opt[Unit]("android") action { 34 | (s, c) => c.copy(androidSupport = true) 35 | } text "generated structures are Parcelable." 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testthrift/OrderEnum.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.2) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | package com.meituan.firefly.testthrift; 8 | 9 | 10 | import java.util.Map; 11 | import java.util.HashMap; 12 | import org.apache.thrift.TEnum; 13 | 14 | public enum OrderEnum implements TEnum { 15 | Order(1), 16 | UnOrder(2), 17 | Mix(3); 18 | 19 | private final int value; 20 | 21 | private OrderEnum(int value) { 22 | this.value = value; 23 | } 24 | 25 | /** 26 | * Get the integer value of this enum value, as defined in the Thrift IDL. 27 | */ 28 | public int getValue() { 29 | return value; 30 | } 31 | 32 | /** 33 | * Find a the enum type by its integer value, as defined in the Thrift IDL. 34 | * @return null if the value is not found. 35 | */ 36 | public static OrderEnum findByValue(int value) { 37 | switch (value) { 38 | case 1: 39 | return Order; 40 | case 2: 41 | return UnOrder; 42 | case 3: 43 | return Mix; 44 | default: 45 | return null; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/NotifyCheck.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import com.meituan.firefly.testthrift.TestException; 4 | import com.meituan.firefly.testthrift.UnionB; 5 | import org.apache.thrift.TException; 6 | 7 | import java.util.List; 8 | 9 | class NotifyCheck implements com.meituan.firefly.testthrift.TestService.Iface { 10 | final int idToCheck; 11 | boolean notified; 12 | 13 | public NotifyCheck(int idToCheck) { 14 | this.idToCheck = idToCheck; 15 | } 16 | 17 | @Override 18 | public void notify(int id) throws TException { 19 | if (id == idToCheck) { 20 | notified = true; 21 | } 22 | } 23 | 24 | @Override 25 | public UnionB get(int id) throws TException { 26 | return null; 27 | } 28 | 29 | @Override 30 | public List getList(List ids) throws TException { 31 | return null; 32 | } 33 | 34 | @Override 35 | public void notifyWithoutOneway(int id) throws TestException, TException { 36 | if (id == idToCheck) { 37 | notified = true; 38 | } 39 | } 40 | @Override 41 | public UnionB emptyArgMethod() throws TestException, TException { 42 | return null; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/TestService.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import org.apache.thrift.TException; 7 | import rx.Observable; 8 | import com.meituan.firefly.annotations.*; 9 | 10 | 11 | public interface TestService { 12 | 13 | @Func(oneway = true, value = { }) 14 | public void notify(@Field(id = 1, required = true, name = "id") Integer id) throws TException; 15 | 16 | @Func(oneway = false, value = { @Field(id = 1, required = false, name = "testException")}) 17 | public UnionB get(@Field(id = 1, required = false, name = "id") Integer id) throws TestException, TException; 18 | 19 | @Func(oneway = false, value = { @Field(id = 1, required = false, name = "testException")}) 20 | public UnionB emptyArgMethod() throws TestException, TException; 21 | 22 | @Func(oneway = false, value = { }) 23 | public List getList(@Field(id = 1, required = false, name = "ids") List ids) throws TException; 24 | 25 | @Func(oneway = false, value = { @Field(id = 1, required = false, name = "testException")}) 26 | public void notifyWithoutOneway(@Field(id = 1, required = true, name = "id") Integer id) throws TestException, TException; 27 | } 28 | -------------------------------------------------------------------------------- /library/src/test/idl/test.thrift: -------------------------------------------------------------------------------- 1 | namespace java com.meituan.firefly.testthrift 2 | 3 | struct OrderedStruct{ 4 | 1: required i32 id; 5 | } 6 | 7 | struct UnorderedStruct{ 8 | 1:required i32 id; 9 | } 10 | 11 | struct MixStruct{ 12 | 1: required i32 id; 13 | 2:required i32 uid; 14 | } 15 | 16 | union UnionB{ 17 | 1:OrderedStruct os; 18 | 2:UnorderedStruct uos; 19 | 3:MixStruct mos; 20 | } 21 | 22 | enum OrderEnum { 23 | Order = 1; 24 | UnOrder = 2; 25 | Mix =3; 26 | } 27 | 28 | struct ComplicatedStruct { 29 | 1:i32 a = 12345; 30 | 2:i64 b = 100; 31 | 3:double c = 99.99; 32 | 4:set shortSet = [1,2,3]; 33 | 5:set intSet = [4,5,6]; 34 | 6:list mixStructlist; 35 | 7:list shortList; 36 | 8:map orderedStructMap; 37 | 9:map mixStructMap; 38 | 10:OrderEnum orderEnum; 39 | 11:binary bin; 40 | } 41 | 42 | exception TestException { 43 | 1:string message; 44 | } 45 | 46 | service TestService{ 47 | oneway void notify(1:required i32 id); 48 | UnionB get(1:i32 id) throws (1:TestException testException); 49 | UnionB emptyArgMethod() throws (1:TestException testException); 50 | list getList(1:list ids); 51 | void notifyWithoutOneway(1:required i32 id) throws (1:TestException testException); 52 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/TestService.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import org.apache.thrift.TException; 7 | import rx.Observable; 8 | import com.meituan.firefly.annotations.*; 9 | 10 | 11 | public interface TestService { 12 | 13 | @Func(oneway = true, value = { }) 14 | public Observable notify(@Field(id = 1, required = true, name = "id") Integer id) throws TException; 15 | 16 | @Func(oneway = false, value = { @Field(id = 1, required = false, name = "testException")}) 17 | public Observable get(@Field(id = 1, required = false, name = "id") Integer id) throws TestException, TException; 18 | 19 | @Func(oneway = false, value = { @Field(id = 1, required = false, name = "testException")}) 20 | public Observable emptyArgMethod() throws TestException, TException; 21 | 22 | @Func(oneway = false, value = { }) 23 | public Observable> getList(@Field(id = 1, required = false, name = "ids") List ids) throws TException; 24 | 25 | @Func(oneway = false, value = { @Field(id = 1, required = false, name = "testException")}) 26 | public Observable notifyWithoutOneway(@Field(id = 1, required = true, name = "id") Integer id) throws TestException, TException; 27 | } 28 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testfirefly/ComplicatedStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class ComplicatedStruct implements Serializable { 11 | 12 | @Field(id = 1, required = false, name = "a") 13 | public Integer a= 12345; 14 | 15 | @Field(id = 2, required = false, name = "b") 16 | public Long b= 100l; 17 | 18 | @Field(id = 3, required = false, name = "c") 19 | public Double c= 99.99; 20 | 21 | @Field(id = 4, required = false, name = "shortSet") 22 | public Set shortSet= new java.util.HashSet(java.util.Arrays.asList((short) 1, (short) 2, (short) 3)); 23 | 24 | @Field(id = 5, required = false, name = "intSet") 25 | public Set intSet= new java.util.HashSet(java.util.Arrays.asList(4, 5, 6)); 26 | 27 | @Field(id = 6, required = false, name = "mixStructlist") 28 | public List mixStructlist; 29 | 30 | @Field(id = 7, required = false, name = "shortList") 31 | public List shortList; 32 | 33 | @Field(id = 8, required = false, name = "orderedStructMap") 34 | public Map orderedStructMap; 35 | 36 | @Field(id = 9, required = false, name = "mixStructMap") 37 | public Map mixStructMap; 38 | 39 | @Field(id = 10, required = false, name = "orderEnum") 40 | public OrderEnum orderEnum; 41 | 42 | @Field(id = 11, required = false, name = "bin") 43 | public byte[] bin; 44 | } -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/rx_testfirefly/ComplicatedStruct.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.rx_testfirefly; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import com.meituan.firefly.annotations.*; 8 | 9 | 10 | public class ComplicatedStruct implements Serializable { 11 | 12 | @Field(id = 1, required = false, name = "a") 13 | public Integer a= 12345; 14 | 15 | @Field(id = 2, required = false, name = "b") 16 | public Long b= 100l; 17 | 18 | @Field(id = 3, required = false, name = "c") 19 | public Double c= 99.99; 20 | 21 | @Field(id = 4, required = false, name = "shortSet") 22 | public Set shortSet= new java.util.HashSet(java.util.Arrays.asList((short) 1, (short) 2, (short) 3)); 23 | 24 | @Field(id = 5, required = false, name = "intSet") 25 | public Set intSet= new java.util.HashSet(java.util.Arrays.asList(4, 5, 6)); 26 | 27 | @Field(id = 6, required = false, name = "mixStructlist") 28 | public List mixStructlist; 29 | 30 | @Field(id = 7, required = false, name = "shortList") 31 | public List shortList; 32 | 33 | @Field(id = 8, required = false, name = "orderedStructMap") 34 | public Map orderedStructMap; 35 | 36 | @Field(id = 9, required = false, name = "mixStructMap") 37 | public Map mixStructMap; 38 | 39 | @Field(id = 10, required = false, name = "orderEnum") 40 | public OrderEnum orderEnum; 41 | 42 | @Field(id = 11, required = false, name = "bin") 43 | public byte[] bin; 44 | } -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/node/Header.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.node 2 | 3 | /** 4 | * A header is either a Thrift include, a C++ include, or a namespace declaration. 5 | *
 6 |  * Header          ::=  Include | CppInclude | Namespace
 7 |  * 
8 | */ 9 | sealed abstract class Header 10 | 11 | /** 12 | * An include makes all the symbols from another file visible (with a prefix) and 13 | * adds corresponding include statements into the code generated for this Thrift document. 14 | *
15 |  *   Include         ::=  'include' Literal
16 |  * 
17 | * @param file the include thrift file 18 | * @param document the document resolved from the thrift file 19 | */ 20 | case class Include(file: String, document: Document) extends Header 21 | 22 | /** 23 | * A namespace declares which namespaces/package/module/etc. 24 | * the type definitions in this file will be declared in for the target languages. 25 | * The namespace scope indicates which language the namespace applies to; 26 | * a scope of '*' indicates that the namespace applies to all target languages. 27 | * 28 | *
29 |  *   Namespace       ::=  ( 'namespace' ( NamespaceScope Identifier ) |
30 |  *                                      ( 'smalltalk.category' STIdentifier ) |
31 |  *                                      ( 'smalltalk.prefix' Identifier ) ) |
32 |  *                        ( 'php_namespace' Literal ) |
33 |  *                        ( 'xsd_namespace' Literal )
34 |  *
35 |  *  NamespaceScope  ::=  '*' | 'cpp' | 'java' | 'py' | 'perl' | 'rb' | 'cocoa' | 'csharp' | 'as3'
36 |  * 
37 | */ 38 | case class NameSpace(scope: String, id: Identifier) extends Header 39 | -------------------------------------------------------------------------------- /lib-android/src/test/java/com/meituan/firefly/android/ParcelableTest.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.android; 2 | 3 | import android.os.Parcel; 4 | 5 | import com.meituan.firefly.android.test.TestEnum; 6 | import com.meituan.firefly.android.test.TestStruct; 7 | 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.robolectric.RobolectricGradleTestRunner; 12 | import org.robolectric.annotation.Config; 13 | 14 | @RunWith(RobolectricGradleTestRunner.class) 15 | @Config(constants = BuildConfig.class, sdk = 21) 16 | public class ParcelableTest { 17 | @Test 18 | public void shouldSupportParcel() throws Exception { 19 | Parcel parcel = Parcel.obtain(); 20 | TestStruct struct = new TestStruct(); 21 | struct.a = 321; 22 | struct.e = TestEnum.A; 23 | struct.bin = new byte[]{123}; 24 | struct.writeToParcel(parcel, 0); 25 | parcel.setDataPosition(0); 26 | TestStruct readStruct = (TestStruct) TestStruct.CREATOR.createFromParcel(parcel); 27 | Assert.assertEquals(struct.a, readStruct.a); 28 | Assert.assertEquals(struct.e, readStruct.e); 29 | Assert.assertArrayEquals(struct.bin, readStruct.bin); 30 | } 31 | 32 | @Test 33 | public void shouldSupportParcelArray() throws Exception { 34 | Parcel parcel = Parcel.obtain(); 35 | TestStruct struct = new TestStruct(); 36 | struct.a = 321; 37 | struct.e = TestEnum.A; 38 | struct.bin = new byte[]{123}; 39 | parcel.writeParcelableArray(new TestStruct[]{new TestStruct(), struct}, 0); 40 | parcel.setDataPosition(0); 41 | TestStruct readStruct = (TestStruct) parcel.readParcelableArray(getClass().getClassLoader())[1]; 42 | Assert.assertEquals(struct.a, readStruct.a); 43 | Assert.assertEquals(struct.e, readStruct.e); 44 | Assert.assertArrayEquals(struct.bin, readStruct.bin); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /gradle_plugin/src/main/groovy/com/meituan/FireflyPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.meituan 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Task 6 | 7 | /*** 8 | * plugin uses idl files in inputDir to generate java files in outputDir via using generator in project firefly 9 | * http://git.sankuai.com/projects/AOPEN/repos/firefly/browse 10 | * the plugin must be declared before plugin com.android.library or android 11 | */ 12 | class FireflyPlugin implements Plugin { 13 | private static final String ANDROID_DEPENDENCY_TASK_NAME = 'preBuild' 14 | private static final String JAVA_DEPENDENCY_TASK_NAME = 'compileJava' 15 | private static String DEPENDENCY_TASK_NAME; 16 | void apply(Project target) { 17 | 18 | target.extensions.create("firefly", FireflyArgs, target) 19 | target.afterEvaluate { 20 | if (target.plugins.hasPlugin('com.android.library') || target.plugins.hasPlugin('android')) { 21 | DEPENDENCY_TASK_NAME=ANDROID_DEPENDENCY_TASK_NAME; 22 | target.android.sourceSets.main.java.srcDir target.firefly.outputDir.absolutePath 23 | } else if (target.plugins.hasPlugin('java')) { 24 | DEPENDENCY_TASK_NAME=JAVA_DEPENDENCY_TASK_NAME; 25 | target.sourceSets.main.java.srcDirs target.firefly.outputDir.absolutePath 26 | } else { 27 | throw new Exception('firefly plugin must used in projects which use com.android.library , android or java plugin ') 28 | } 29 | FireflyTask fireflyTask = target.tasks.create("thrift2java", FireflyTask) 30 | def Task dependentTask = target.tasks.findByName(DEPENDENCY_TASK_NAME) 31 | if (dependentTask != null) { 32 | fireflyTask.dependsOn dependentTask.taskDependencies.getDependencies(dependentTask) 33 | dependentTask.dependsOn fireflyTask 34 | } else { 35 | throw new Exception('cannot find depenpent task ' + DEPENDENCY_TASK_NAME) 36 | } 37 | 38 | } 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/ParserException.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly 2 | 3 | class ParseException(reason: String, cause: Throwable) extends Exception(reason, cause) { 4 | def this(reason: String) = this(reason, null) 5 | } 6 | 7 | // severe errors 8 | class NegativeFieldIdException(name: String) 9 | extends ParseException("Negative user-provided id in field " + name) 10 | 11 | class DuplicateFieldIdException(name: String) 12 | extends ParseException("Duplicate user-provided id in field " + name) 13 | 14 | class RepeatingEnumValueException(name: String, value: Int) 15 | extends ParseException("Repeating enum value in " + name + ": " + value) 16 | 17 | class UnionFieldInvalidNameException(union: String, field: String) 18 | extends ParseException("Field " + field + " in union " + union + " is prohibited") 19 | 20 | 21 | // warnings (non-severe errors). If the strict mode is on, Scrooge will throw these exceptions; 22 | // otherwise it merely prints warnings. 23 | class ParseWarning(reason: String, cause: Throwable) 24 | extends ParseException(reason, cause) { 25 | def this(reason: String) = this(reason, null) 26 | } 27 | 28 | class UnionFieldRequiredException(union: String, field: String) 29 | extends ParseWarning("Field " + field + " in union " + union + " cannot be required") 30 | 31 | class UnionFieldOptionalException(union: String, field: String) 32 | extends ParseWarning("Field " + field + " in union " + union + " cannot be optional") 33 | 34 | object UnionFieldRequirednessException { 35 | def apply(union: String, field: String, requiredness: String): ParseWarning = { 36 | requiredness.toLowerCase match { 37 | case "required" => new UnionFieldRequiredException(union, field) 38 | case "optional" => new UnionFieldOptionalException(union, field) 39 | } 40 | } 41 | } 42 | 43 | class InvalidThriftFilenameException(filename: String, regex: String) 44 | extends ParseWarning("Thrift filename " + filename + " is invalid, did not pass this check: " + regex) 45 | 46 | class KeywordException(id: String) 47 | extends ParseWarning(s"Identifier '$id' is invalid: it is a thrift keyword.") 48 | 49 | -------------------------------------------------------------------------------- /gradle_plugin/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' 7 | } 8 | } 9 | apply plugin: 'groovy' 10 | apply plugin: 'maven-publish' 11 | apply plugin: 'com.jfrog.bintray' 12 | 13 | repositories { 14 | mavenLocal() 15 | jcenter() 16 | mavenCentral() 17 | } 18 | dependencies { 19 | compile gradleApi() 20 | compile localGroovy() 21 | testCompile 'junit:junit:4.12' 22 | compile 'com.meituan.firefly:generator_2.11:0.2.4' 23 | } 24 | 25 | group = 'com.meituan.firefly' 26 | version = '0.2.4' 27 | description = 'Gradle plugin generates thrift idls to codes using firefly generator.' 28 | 29 | task docJar(type: Jar, dependsOn: javadoc) { 30 | from tasks.javadoc.destinationDir 31 | classifier 'doc' 32 | } 33 | 34 | task sourceJar(type: Jar) { 35 | from sourceSets.main.allSource 36 | classifier 'sources' 37 | } 38 | 39 | def isSnapshot() { 40 | return version.endsWith("SNAPSHOT") 41 | } 42 | 43 | publishing { 44 | repositories { 45 | maven { 46 | if (isSnapshot()) { 47 | url mavenSnapshotRepo 48 | } else { 49 | url mavenReleaseRepo 50 | } 51 | credentials { 52 | username = mavenUsername 53 | password = mavenPassword 54 | } 55 | } 56 | } 57 | 58 | publications { 59 | plugin(MavenPublication) { 60 | from components.java 61 | artifactId = 'gradle-plugin' 62 | artifact docJar 63 | artifact sourceJar 64 | pom.withXml { 65 | asNode().appendNode('description', '${description}') 66 | } 67 | } 68 | } 69 | } 70 | 71 | bintray { 72 | user = bintrayUser 73 | key = bintrayKey 74 | publications = ['plugin'] 75 | pkg { 76 | repo = 'maven' 77 | name = 'com.meituan.firefly:gradle-plugin' 78 | userOrg = user 79 | licenses = ['Apache-2.0'] 80 | vcsUrl = 'https://github.com/meituan/Firefly.git' 81 | publish = true 82 | version { 83 | name = '0.2.4' 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/adapters/EnumTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.adapters; 2 | 3 | import com.meituan.firefly.Thrift; 4 | import com.meituan.firefly.TypeAdapter; 5 | import com.meituan.firefly.Types; 6 | import org.apache.thrift.TException; 7 | import org.apache.thrift.protocol.TProtocol; 8 | import org.apache.thrift.protocol.TType; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | import java.lang.reflect.Type; 13 | 14 | public class EnumTypeAdapterFactory implements TypeAdapter.TypeAdapterFactory { 15 | @Override 16 | public TypeAdapter create(Type type, Thrift thrift) { 17 | Class rawType = Types.getRawType(type); 18 | if (!rawType.isEnum()) { 19 | return null; 20 | } 21 | return new EnumTypeAdapter(rawType); 22 | } 23 | 24 | static class EnumTypeAdapter implements TypeAdapter { 25 | private final Method getValueMethod; 26 | private final Method findByValueMehtod; 27 | 28 | EnumTypeAdapter(Class rawType) { 29 | try { 30 | getValueMethod = rawType.getMethod("getValue"); 31 | findByValueMehtod = rawType.getMethod("findByValue", int.class); 32 | } catch (NoSuchMethodException e) { 33 | throw new IllegalArgumentException(e); 34 | } 35 | } 36 | 37 | @Override 38 | public void write(Object o, TProtocol protocol) throws TException { 39 | try { 40 | protocol.writeI32((Integer) getValueMethod.invoke(o)); 41 | } catch (IllegalAccessException | InvocationTargetException e) { 42 | throw new IllegalStateException(e); 43 | } 44 | } 45 | 46 | @Override 47 | public Object read(TProtocol protocol) throws TException { 48 | int value = protocol.readI32(); 49 | try { 50 | return findByValueMehtod.invoke(null, value); 51 | } catch (IllegalAccessException | InvocationTargetException e) { 52 | throw new IllegalStateException(e); 53 | } 54 | } 55 | 56 | @Override 57 | public byte getTType() { 58 | return TType.I32; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/adapters/SetTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.adapters; 2 | 3 | import com.meituan.firefly.Thrift; 4 | import com.meituan.firefly.TypeAdapter; 5 | import com.meituan.firefly.Types; 6 | import org.apache.thrift.TException; 7 | import org.apache.thrift.protocol.TProtocol; 8 | import org.apache.thrift.protocol.TSet; 9 | import org.apache.thrift.protocol.TType; 10 | 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.HashSet; 14 | import java.util.Set; 15 | 16 | public class SetTypeAdapterFactory implements TypeAdapter.TypeAdapterFactory { 17 | @Override 18 | public TypeAdapter create(Type type, Thrift thrift) { 19 | Class rawType = Types.getRawType(type); 20 | if (!Set.class.isAssignableFrom(rawType)) { 21 | return null; 22 | } 23 | 24 | if (!(type instanceof ParameterizedType)) { 25 | throw new IllegalArgumentException("set field must be parameterized"); 26 | } 27 | Type valueType = ((ParameterizedType) type).getActualTypeArguments()[0]; 28 | TypeAdapter valueTypeAdapter = thrift.getAdapter(valueType); 29 | return new SetTypeAdapter(valueTypeAdapter); 30 | } 31 | 32 | static class SetTypeAdapter implements TypeAdapter { 33 | private final TypeAdapter valueTypeAdapter; 34 | 35 | SetTypeAdapter(TypeAdapter valueTypeAdapter) { 36 | this.valueTypeAdapter = valueTypeAdapter; 37 | } 38 | 39 | @Override 40 | public void write(Set set, TProtocol protocol) throws TException { 41 | protocol.writeSetBegin(new TSet(valueTypeAdapter.getTType(), set.size())); 42 | for (Object o : set) { 43 | valueTypeAdapter.write(o, protocol); 44 | } 45 | protocol.writeSetEnd(); 46 | } 47 | 48 | @Override 49 | public Set read(TProtocol protocol) throws TException { 50 | TSet tset = protocol.readSetBegin(); 51 | HashSet hashSet = new HashSet(tset.size); 52 | for (int i = 0, n = tset.size; i < n; i++) { 53 | hashSet.add(valueTypeAdapter.read(protocol)); 54 | } 55 | protocol.readSetEnd(); 56 | return hashSet; 57 | } 58 | 59 | @Override 60 | public byte getTType() { 61 | return TType.SET; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/adapters/ListTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.adapters; 2 | 3 | import com.meituan.firefly.Thrift; 4 | import com.meituan.firefly.TypeAdapter; 5 | import com.meituan.firefly.Types; 6 | import org.apache.thrift.TException; 7 | import org.apache.thrift.protocol.TList; 8 | import org.apache.thrift.protocol.TProtocol; 9 | import org.apache.thrift.protocol.TType; 10 | 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class ListTypeAdapterFactory implements TypeAdapter.TypeAdapterFactory { 17 | @Override 18 | public TypeAdapter create(Type type, Thrift thrift) { 19 | Class rawType = Types.getRawType(type); 20 | if (!List.class.isAssignableFrom(rawType)) { 21 | return null; 22 | } 23 | 24 | if (!(type instanceof ParameterizedType)) { 25 | throw new IllegalArgumentException("list field must be parameterized"); 26 | } 27 | Type valueType = ((ParameterizedType) type).getActualTypeArguments()[0]; 28 | TypeAdapter valueTypeAdapter = thrift.getAdapter(valueType); 29 | return new ListTypeAdapter(valueTypeAdapter); 30 | } 31 | 32 | static class ListTypeAdapter implements TypeAdapter { 33 | private final TypeAdapter valueTypeAdapter; 34 | 35 | ListTypeAdapter(TypeAdapter valueTypeAdapter) { 36 | this.valueTypeAdapter = valueTypeAdapter; 37 | } 38 | 39 | @Override 40 | public void write(List list, TProtocol protocol) throws TException { 41 | protocol.writeListBegin(new TList(valueTypeAdapter.getTType(), list.size())); 42 | for (Object o : list) { 43 | valueTypeAdapter.write(o, protocol); 44 | } 45 | protocol.writeListEnd(); 46 | } 47 | 48 | @Override 49 | public List read(TProtocol protocol) throws TException { 50 | TList tlist = protocol.readListBegin(); 51 | ArrayList arrayList = new ArrayList(tlist.size); 52 | for (int i = 0, n = tlist.size; i < n; i++) { 53 | arrayList.add(valueTypeAdapter.read(protocol)); 54 | } 55 | protocol.readListEnd(); 56 | return arrayList; 57 | } 58 | 59 | @Override 60 | public byte getTType() { 61 | return TType.LIST; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib-android/src/main/java/com/meituan/firefly/android/StructBase.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.android; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.meituan.firefly.Thrift; 7 | import com.meituan.firefly.TypeAdapter; 8 | 9 | import org.apache.thrift.TException; 10 | import org.apache.thrift.protocol.TBinaryProtocol; 11 | import org.apache.thrift.protocol.TProtocol; 12 | import org.apache.thrift.transport.TIOStreamTransport; 13 | import org.apache.thrift.transport.TTransport; 14 | 15 | import java.io.ByteArrayInputStream; 16 | import java.io.ByteArrayOutputStream; 17 | import java.io.Serializable; 18 | import java.lang.reflect.Array; 19 | 20 | /** 21 | * Super class for all generated structures supports Android Parcelable. 22 | */ 23 | public abstract class StructBase implements Parcelable, Serializable { 24 | public static class Creator implements Parcelable.Creator { 25 | private final Class tClass; 26 | 27 | public Creator(Class tClass) { 28 | this.tClass = tClass; 29 | } 30 | 31 | @Override 32 | public T createFromParcel(Parcel source) { 33 | byte[] bytes = source.createByteArray(); 34 | TTransport transport = new TIOStreamTransport(new ByteArrayInputStream(bytes)); 35 | TProtocol protocol = new TBinaryProtocol(transport); 36 | TypeAdapter typeAdapter = Thrift.instance.getAdapter(tClass); 37 | try { 38 | return typeAdapter.read(protocol); 39 | } catch (TException e) { 40 | throw new RuntimeException(e); 41 | } 42 | } 43 | 44 | @Override 45 | public T[] newArray(int size) { 46 | return (T[]) Array.newInstance(tClass, size); 47 | } 48 | } 49 | 50 | @Override 51 | public int describeContents() { 52 | return 0; 53 | } 54 | 55 | @Override 56 | public void writeToParcel(Parcel dest, int flags) { 57 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 58 | TTransport transport = new TIOStreamTransport(stream); 59 | TProtocol protocol = new TBinaryProtocol(transport); 60 | TypeAdapter typeAdapter = Thrift.instance.getAdapter(this.getClass()); 61 | try { 62 | typeAdapter.write(this, protocol); 63 | } catch (TException e) { 64 | throw new RuntimeException(e); 65 | } 66 | dest.writeByteArray(stream.toByteArray()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib-android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib-android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:1.5.0' 7 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 8 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | apply plugin: 'com.android.library' 15 | apply plugin: 'com.github.dcendents.android-maven' 16 | apply plugin: 'com.jfrog.bintray' 17 | 18 | group = 'com.meituan.firefly' 19 | version = '0.2.4' 20 | description = 'Runtime library for firefly generated codes, provides Superclass supports Parcelable for all generated struct classes.' 21 | 22 | repositories { 23 | mavenLocal() 24 | jcenter() 25 | mavenCentral() 26 | } 27 | android { 28 | compileSdkVersion 23 29 | buildToolsVersion "23.0.2" 30 | 31 | defaultConfig { 32 | minSdkVersion 9 33 | versionCode 1 34 | versionName "0.2.4" 35 | } 36 | buildTypes { 37 | release { 38 | minifyEnabled false 39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 40 | } 41 | } 42 | } 43 | 44 | dependencies { 45 | compile 'com.meituan.firefly:library:0.2.4' 46 | testCompile 'junit:junit:4.12' 47 | testCompile "org.robolectric:robolectric:3.0" 48 | testCompile "org.slf4j:slf4j-simple:1.7.12" 49 | } 50 | 51 | def isSnapshot() { 52 | return version.endsWith("SNAPSHOT") 53 | } 54 | 55 | uploadArchives { 56 | repositories { 57 | mavenDeployer { 58 | repository(url: isSnapshot() ? mavenSnapshotRepo : mavenReleaseRepo) { 59 | authentication(userName: mavenUsername, password: mavenPassword) 60 | } 61 | } 62 | } 63 | } 64 | 65 | bintray { 66 | user = bintrayUser 67 | key = bintrayKey 68 | configurations = ['archives'] 69 | pkg { 70 | repo = 'maven' 71 | name = 'com.meituan.firefly:lib-android' 72 | userOrg = user 73 | licenses = ['Apache-2.0'] 74 | vcsUrl = 'https://github.com/meituan/Firefly.git' 75 | publish = true 76 | version { 77 | name = '0.2.4' 78 | } 79 | } 80 | } 81 | 82 | task sourcesJar(type: Jar) { 83 | from android.sourceSets.main.java.srcDirs 84 | classifier = 'sources' 85 | } 86 | 87 | task javadoc(type: Javadoc) { 88 | source = android.sourceSets.main.java.srcDirs 89 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 90 | } 91 | 92 | task javadocJar(type: Jar, dependsOn: javadoc) { 93 | classifier = 'javadoc' 94 | from javadoc.destinationDir 95 | } 96 | artifacts { 97 | archives javadocJar 98 | archives sourcesJar 99 | } 100 | 101 | 102 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/adapters/MapTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.adapters; 2 | 3 | import com.meituan.firefly.Thrift; 4 | import com.meituan.firefly.TypeAdapter; 5 | import com.meituan.firefly.Types; 6 | import org.apache.thrift.TException; 7 | import org.apache.thrift.protocol.TMap; 8 | import org.apache.thrift.protocol.TProtocol; 9 | import org.apache.thrift.protocol.TType; 10 | 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class MapTypeAdapterFactory implements TypeAdapter.TypeAdapterFactory { 17 | 18 | @Override 19 | public TypeAdapter create(Type type, Thrift thrift) { 20 | Class rawType = Types.getRawType(type); 21 | if (!Map.class.isAssignableFrom(rawType)) { 22 | return null; 23 | } 24 | if (!(type instanceof ParameterizedType)) { 25 | throw new IllegalArgumentException("map field must be parameterized"); 26 | } 27 | Type[] parameterTypes = ((ParameterizedType) type).getActualTypeArguments(); 28 | Type keyType = parameterTypes[0]; 29 | Type valueType = parameterTypes[1]; 30 | TypeAdapter keyTypeAdapter = thrift.getAdapter(keyType); 31 | TypeAdapter valueTypeAdapter = thrift.getAdapter(valueType); 32 | return new MapTypeAdapter(keyTypeAdapter, valueTypeAdapter); 33 | } 34 | 35 | static class MapTypeAdapter implements TypeAdapter> { 36 | private final TypeAdapter keyTypeAdapter; 37 | private final TypeAdapter valueTypeAdapter; 38 | 39 | MapTypeAdapter(TypeAdapter keyTypeAdapter, TypeAdapter valueTypeAdapter) { 40 | this.keyTypeAdapter = keyTypeAdapter; 41 | this.valueTypeAdapter = valueTypeAdapter; 42 | } 43 | 44 | @Override 45 | public void write(Map map, TProtocol protocol) throws TException { 46 | protocol.writeMapBegin(new TMap(keyTypeAdapter.getTType(), valueTypeAdapter.getTType(), map.size())); 47 | for (Map.Entry entry : map.entrySet()) { 48 | keyTypeAdapter.write(entry.getKey(), protocol); 49 | valueTypeAdapter.write(entry.getValue(), protocol); 50 | } 51 | protocol.writeMapEnd(); 52 | } 53 | 54 | @Override 55 | public Map read(TProtocol protocol) throws TException { 56 | TMap tmap = protocol.readMapBegin(); 57 | HashMap hashMap = new HashMap(tmap.size); 58 | for (int i = 0, n = tmap.size; i < n; i++) { 59 | Object key = keyTypeAdapter.read(protocol); 60 | Object value = valueTypeAdapter.read(protocol); 61 | hashMap.put(key, value); 62 | } 63 | protocol.readMapEnd(); 64 | return hashMap; 65 | } 66 | 67 | @Override 68 | public byte getTType() { 69 | return TType.MAP; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/OkhttpTransport.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import com.squareup.okhttp.*; 4 | import org.apache.thrift.TByteArrayOutputStream; 5 | import org.apache.thrift.transport.TTransport; 6 | import org.apache.thrift.transport.TTransportException; 7 | 8 | import java.io.IOException; 9 | 10 | /** 11 | * Http Thrift Transport implementation, using Okhttp 12 | */ 13 | public class OkhttpTransport extends TTransport { 14 | public static final MediaType CONTENT_TYPE = MediaType.parse("application/x-thrift"); 15 | private final String url; 16 | private final OkHttpClient client; 17 | private byte[] readBuffer; 18 | private final TByteArrayOutputStream writeBuffer = new TByteArrayOutputStream(); 19 | private volatile boolean used; 20 | private int readBufferPosition; 21 | 22 | public OkhttpTransport(String url, OkHttpClient client) { 23 | this.url = url; 24 | this.client = client; 25 | } 26 | 27 | @Override 28 | public boolean isOpen() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public void open() throws TTransportException { 34 | } 35 | 36 | @Override 37 | public void close() { 38 | } 39 | 40 | @Override 41 | public int read(byte[] buf, int off, int len) throws TTransportException { 42 | int bytesRemaining = getBytesRemainingInBuffer(); 43 | int amtToRead = (len > bytesRemaining ? bytesRemaining : len); 44 | if (amtToRead > 0) { 45 | System.arraycopy(readBuffer, readBufferPosition, buf, off, amtToRead); 46 | consumeBuffer(amtToRead); 47 | } 48 | return amtToRead; 49 | } 50 | 51 | @Override 52 | public void write(byte[] buf, int off, int len) throws TTransportException { 53 | writeBuffer.write(buf, off, len); 54 | } 55 | 56 | @Override 57 | public void flush() throws TTransportException { 58 | if (used) { 59 | throw new IllegalStateException("transport already used"); 60 | } 61 | used = true; 62 | Request request = new Request.Builder().url(url).post(RequestBody.create(CONTENT_TYPE, writeBuffer.get(), 0, writeBuffer.len())) 63 | .addHeader("Accept", "application/x-thrift").build(); 64 | try { 65 | Response response = client.newCall(request).execute(); 66 | if (!response.isSuccessful()) { 67 | throw new TTransportException("response is not successful"); 68 | } 69 | readBuffer = response.body().bytes(); 70 | } catch (IOException e) { 71 | throw new TTransportException(e); 72 | } 73 | } 74 | 75 | @Override 76 | public byte[] getBuffer() { 77 | return readBuffer; 78 | } 79 | 80 | @Override 81 | public int getBufferPosition() { 82 | return readBufferPosition; 83 | } 84 | 85 | @Override 86 | public int getBytesRemainingInBuffer() { 87 | return readBuffer.length - readBufferPosition; 88 | } 89 | 90 | @Override 91 | public void consumeBuffer(int len) { 92 | readBufferPosition += len; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/SerializationTest.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import com.meituan.firefly.testfirefly.ComplicatedStruct; 4 | import com.meituan.firefly.testfirefly.MixStruct; 5 | import com.meituan.firefly.testfirefly.OrderEnum; 6 | import com.meituan.firefly.testfirefly.OrderedStruct; 7 | import org.assertj.core.api.Condition; 8 | import org.junit.Test; 9 | 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.ObjectInputStream; 13 | import java.io.ObjectOutputStream; 14 | import java.util.Arrays; 15 | import java.util.HashMap; 16 | import java.util.HashSet; 17 | import java.util.Map; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | public class SerializationTest { 22 | @Test 23 | public void shouldSupportSerialization() throws Exception { 24 | ComplicatedStruct complicatedStruct = new ComplicatedStruct(); 25 | assertThat(complicatedStruct.a).isEqualTo(12345); 26 | assertThat(complicatedStruct.b).isEqualTo(100l); 27 | assertThat(complicatedStruct.c).isEqualTo(99.99); 28 | complicatedStruct.a = 54321; 29 | complicatedStruct.shortSet = new HashSet<>(Arrays.asList((short) 10, (short) 20)); 30 | complicatedStruct.intSet = new HashSet<>(Arrays.asList(30, 40)); 31 | complicatedStruct.shortList = Arrays.asList((short) 100, (short) 101); 32 | MixStruct mixStruct1 = new MixStruct(); 33 | mixStruct1.id = 1; 34 | mixStruct1.uid = 2; 35 | complicatedStruct.mixStructlist = Arrays.asList(mixStruct1); 36 | OrderedStruct orderedStruct1 = new OrderedStruct(); 37 | orderedStruct1.id = 1; 38 | Map orderedStructMap = new HashMap<>(); 39 | orderedStructMap.put((short) 1, orderedStruct1); 40 | complicatedStruct.orderedStructMap = orderedStructMap; 41 | MixStruct mixStruct2 = new MixStruct(); 42 | mixStruct2.id = 1; 43 | mixStruct2.uid = 3; 44 | Map mixStructMap = new HashMap<>(); 45 | mixStructMap.put((short) 1, mixStruct2); 46 | complicatedStruct.mixStructMap = mixStructMap; 47 | complicatedStruct.orderEnum = OrderEnum.Mix; 48 | complicatedStruct.bin = new byte[]{1, 2, 3}; 49 | 50 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 51 | ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream); 52 | outputStream.writeObject(complicatedStruct); 53 | ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); 54 | ComplicatedStruct deserializedComplicatedStruct = (ComplicatedStruct) inputStream.readObject(); 55 | 56 | assertThat(deserializedComplicatedStruct).isNotNull(); 57 | assertThat(deserializedComplicatedStruct.a).isEqualTo(54321); 58 | assertThat(deserializedComplicatedStruct.b).isEqualTo(100l); 59 | assertThat(deserializedComplicatedStruct.c).isEqualTo(99.99); 60 | assertThat(deserializedComplicatedStruct.shortSet).containsOnly((short) 10, (short) 20); 61 | assertThat(deserializedComplicatedStruct.intSet).containsOnly(30, 40); 62 | assertThat(deserializedComplicatedStruct.shortList).containsOnly((short) 100, (short) 101); 63 | assertThat(deserializedComplicatedStruct.mixStructlist).hasSize(1).hasOnlyElementsOfType(MixStruct.class); 64 | assertThat(deserializedComplicatedStruct.orderedStructMap).hasSize(1).containsOnlyKeys((short) 1); 65 | assertThat(deserializedComplicatedStruct.orderedStructMap.get((short) 1)).isExactlyInstanceOf(OrderedStruct.class).is(new Condition() { 66 | @Override 67 | public boolean matches(OrderedStruct value) { 68 | return value.id == 1; 69 | } 70 | }); 71 | assertThat(deserializedComplicatedStruct.mixStructMap).hasSize(1).containsOnlyKeys((short) 1); 72 | assertThat(deserializedComplicatedStruct.mixStructMap.get((short) 1)).isExactlyInstanceOf(MixStruct.class).is(new Condition() { 73 | @Override 74 | public boolean matches(MixStruct value) { 75 | return value.id == 1 && value.uid == 3; 76 | } 77 | }); 78 | assertThat(deserializedComplicatedStruct.orderEnum).isEqualTo(OrderEnum.Mix); 79 | assertThat(deserializedComplicatedStruct.bin).isEqualTo(new byte[]{1, 2, 3}); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/ThriftTest.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import com.meituan.firefly.testfirefly.TestService; 4 | import com.meituan.firefly.testthrift.TestService.Iface; 5 | import org.apache.thrift.TException; 6 | import org.apache.thrift.protocol.TBinaryProtocol; 7 | import org.apache.thrift.protocol.TProtocol; 8 | import org.apache.thrift.transport.TMemoryInputTransport; 9 | import org.apache.thrift.transport.TTransport; 10 | import org.assertj.core.api.Assertions; 11 | import org.junit.Assert; 12 | import org.junit.Test; 13 | 14 | import java.lang.reflect.Method; 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | public class ThriftTest { 20 | Thrift thrift = Thrift.instance; 21 | 22 | @Test 23 | public void shouldCreateService() { 24 | TestService service = thrift.create(TestService.class, new Thrift.SimpleTProtocolFactory() { 25 | @Override 26 | public TProtocol get() { 27 | return new TBinaryProtocol(new TMemoryInputTransport(new byte[]{})); 28 | } 29 | }); 30 | assertThat(service).isNotNull(); 31 | } 32 | 33 | @Test 34 | public void testInterceptorOrder() throws Exception { 35 | AtomicInteger ai = new AtomicInteger(); 36 | OrderCheckInterceptor interceptor1 = new OrderCheckInterceptor(ai); 37 | OrderCheckInterceptor interceptor2 = new OrderCheckInterceptor(ai); 38 | final TTransport transport = new FlushableMemoryBuffer(4096); 39 | com.meituan.firefly.testfirefly.TestService testService = thrift.create(com.meituan.firefly.testfirefly.TestService.class, new Thrift.SimpleTProtocolFactory() { 40 | @Override 41 | public TProtocol get() { 42 | return new TBinaryProtocol(transport); 43 | } 44 | }, interceptor1, interceptor2); 45 | NotifyCheck notifyCheck = new NotifyCheck(100); 46 | com.meituan.firefly.testthrift.TestService.Processor processor = new com.meituan.firefly.testthrift.TestService.Processor(notifyCheck); 47 | testService.notify(100); 48 | processor.process(new TBinaryProtocol(transport), new TBinaryProtocol(transport)); 49 | Assert.assertTrue(notifyCheck.notified); 50 | Assertions.assertThat(new int[]{interceptor2.inorder, interceptor1.inorder, interceptor1.outorder, interceptor2.outorder}).isEqualTo(new int[]{0, 1, 2, 3}); 51 | } 52 | 53 | @Test 54 | public void testInterceptorAbort() throws Exception { 55 | final TTransport transport = new FlushableMemoryBuffer(4096); 56 | final com.meituan.firefly.testfirefly.TestService testService = thrift.create(com.meituan.firefly.testfirefly.TestService.class, new Thrift.SimpleTProtocolFactory() { 57 | @Override 58 | public TProtocol get() { 59 | return new TBinaryProtocol(transport); 60 | } 61 | }, new Interceptor() { 62 | @Override 63 | public Object intercept(Method method, Object[] args, TProtocol protocol, int seqId, Processor processor) throws Throwable { 64 | return null; 65 | } 66 | }); 67 | NotifyCheck notifyCheck = new NotifyCheck(100); 68 | com.meituan.firefly.testthrift.TestService.Processor processor = new com.meituan.firefly.testthrift.TestService.Processor(notifyCheck); 69 | testService.notify(100); 70 | try { 71 | processor.process(new TBinaryProtocol(transport), new TBinaryProtocol(transport)); 72 | Assert.assertFalse(notifyCheck.notified); 73 | } catch (TException e) { 74 | } 75 | Assertions.assertThat(notifyCheck.notified).isFalse(); 76 | } 77 | 78 | private static class OrderCheckInterceptor implements Interceptor { 79 | private final AtomicInteger ai; 80 | public int inorder; 81 | public int outorder; 82 | 83 | public OrderCheckInterceptor(AtomicInteger ai) { 84 | this.ai = ai; 85 | } 86 | 87 | @Override 88 | public Object intercept(Method method, Object[] args, TProtocol protocol, int seqId, Processor processor) throws Throwable { 89 | inorder = ai.getAndAdd(1); 90 | Object object = processor.process(method, args, protocol, seqId); 91 | outorder = ai.getAndAdd(1); 92 | return object; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /gradle_plugin/src/main/groovy/com/meituan/FireflyTask.groovy: -------------------------------------------------------------------------------- 1 | package com.meituan 2 | 3 | import org.gradle.api.DefaultTask 4 | import com.meituan.firefly.Main$ 5 | import org.gradle.api.tasks.Input 6 | import org.gradle.api.tasks.InputDirectory 7 | import org.gradle.api.tasks.OutputDirectory 8 | import org.gradle.api.tasks.TaskAction 9 | import org.gradle.api.tasks.incremental.IncrementalTaskInputs 10 | 11 | class FireflyTask extends DefaultTask { 12 | 13 | File inputDir = new File("${project.projectDir}/src/main/idl") 14 | File outputDir = new File("${project.buildDir}/generated/source/firefly") 15 | boolean rxStyle = false 16 | boolean android = false 17 | 18 | public static final String CONFIG_MAP = 'config_map.txt' 19 | public static final String CONFIG_MAP_SIGN = '->' 20 | File configMapFile 21 | 22 | FireflyTask() { 23 | inputDir = (getProject().firefly.inputDir) 24 | outputDir = (getProject().firefly.outputDir) 25 | rxStyle= (getProject().firefly.rxStyle) 26 | android=(getProject().firefly.android) 27 | } 28 | /** 29 | * 30 | * for test 31 | */ 32 | static void main(args) { 33 | 34 | println 'begin' 35 | def array = ['${project.buildDir}/src/main/idl/PoiComment.thrift', '--output', '/Users/zhangmeng/Desktop/common/', '--rx'] as String[]; 36 | Main$.MODULE$.main(array); 37 | println 'finish' 38 | 39 | } 40 | 41 | @TaskAction 42 | def generator(IncrementalTaskInputs inputs) throws Exception { 43 | if (!outputDir.exists()) 44 | outputDir.mkdirs() 45 | 46 | configMapFile = new File(outputDir, CONFIG_MAP) 47 | configMapFile.absolutePath 48 | inputs.outOfDate { change -> 49 | if (!change.file.name.equals(CONFIG_MAP)) { 50 | invokeGenerator(change.file.path) 51 | recordFilePackageName() 52 | } 53 | } 54 | inputs.removed { change -> 55 | deleteFile(change.file.name) 56 | } 57 | } 58 | 59 | def void recordFilePackageName() { 60 | if (!configMapFile.exists()) { 61 | configMapFile.createNewFile() 62 | } 63 | for (File file : inputDir.listFiles()) 64 | file.eachLine { 65 | line -> 66 | if (line.contains('namespace') && line.contains('java')) { 67 | String packageName = line.substring(line.indexOf('java') + 4).trim() 68 | if (!configMapFile.text.contains(packageName)) 69 | configMapFile.append(file.name + CONFIG_MAP_SIGN + packageName + '\n') 70 | } 71 | 72 | }; 73 | } 74 | 75 | def void deleteFile(String name) { 76 | String packageName = getPackageName(name.subSequence(0, name.indexOf('.'))) 77 | if (null == packageName) { 78 | return 79 | } 80 | def File targetFile = new File(outputDir, packageName.replace('.', '/')) 81 | if (!targetFile.parentFile.deleteDir()) { 82 | println('delete file encounter error') 83 | } 84 | targetFile.delete() 85 | StringBuilder mapBuilder = new StringBuilder() 86 | configMapFile.eachLine { 87 | line -> 88 | if (!line.contains(packageName)) { 89 | mapBuilder.append(line + '\n') 90 | } 91 | } 92 | configMapFile.text = mapBuilder.toString() 93 | } 94 | 95 | def String getPackageName(String name) { 96 | if (!configMapFile.exists()) { 97 | recordFilePackageName(); 98 | } 99 | String pachageName 100 | configMapFile.eachLine { 101 | line -> 102 | if (line.contains(name)) { 103 | pachageName = line.split(CONFIG_MAP_SIGN)[1] 104 | } 105 | } 106 | pachageName 107 | } 108 | 109 | def invokeGenerator(String thriftFilePath) throws Exception { 110 | ArrayList thriftArgslist = new ArrayList() 111 | thriftArgslist.add(thriftFilePath) 112 | thriftArgslist.add('--output') 113 | thriftArgslist.add(outputDir.path) 114 | if (rxStyle) 115 | thriftArgslist.add('--rx'); 116 | if (android) 117 | thriftArgslist.add('--android'); 118 | try { 119 | Main$.MODULE$.main(thriftArgslist as String[]); 120 | } catch (Exception e) { 121 | throw e 122 | } 123 | } 124 | 125 | @InputDirectory 126 | File getInputDir() { 127 | return inputDir 128 | } 129 | 130 | @OutputDirectory 131 | File getOutputDir() { 132 | return outputDir 133 | } 134 | 135 | @InputDirectory 136 | void setInputDir(File inputDir) { 137 | this.inputDir = inputDir 138 | } 139 | 140 | @OutputDirectory 141 | void setOutputDir(File outputDir) { 142 | this.outputDir = outputDir 143 | } 144 | @Input 145 | boolean getAndroid() { 146 | return android 147 | } 148 | @Input 149 | boolean getRxStyle() { 150 | return rxStyle 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib-android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/TypeAdapter.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import org.apache.thrift.TException; 4 | import org.apache.thrift.protocol.TProtocol; 5 | import org.apache.thrift.protocol.TType; 6 | 7 | import java.lang.reflect.Type; 8 | import java.nio.ByteBuffer; 9 | 10 | /** 11 | * Write an object of specified type into protocol, or read an object of specified type from protocol. 12 | */ 13 | public interface TypeAdapter { 14 | /** 15 | * Write an object of type T into protocol 16 | * 17 | * @param t the object to write 18 | * @param protocol protocol to write into 19 | */ 20 | void write(T t, TProtocol protocol) throws TException; 21 | 22 | /** 23 | * Read an object of type T from protocol 24 | * 25 | * @param protocol the protocol to read from 26 | */ 27 | T read(TProtocol protocol) throws TException; 28 | 29 | /** 30 | * @return Thrift {@link TType} of the specified type 31 | */ 32 | byte getTType(); 33 | 34 | interface TypeAdapterFactory { 35 | TypeAdapter create(Type type, Thrift thrift); 36 | } 37 | 38 | TypeAdapter BOOLEAN_TYPE_ADAPTER = new TypeAdapter() { 39 | @Override 40 | public void write(Boolean aBoolean, TProtocol protocol) throws TException { 41 | protocol.writeBool(aBoolean); 42 | } 43 | 44 | @Override 45 | public Boolean read(TProtocol protocol) throws TException { 46 | return protocol.readBool(); 47 | } 48 | 49 | @Override 50 | public byte getTType() { 51 | return TType.BOOL; 52 | } 53 | }; 54 | TypeAdapter BYTE_TYPE_ADAPTER = new TypeAdapter() { 55 | @Override 56 | public void write(Byte aByte, TProtocol protocol) throws TException { 57 | protocol.writeByte(aByte); 58 | } 59 | 60 | @Override 61 | public Byte read(TProtocol protocol) throws TException { 62 | return protocol.readByte(); 63 | } 64 | 65 | @Override 66 | public byte getTType() { 67 | return TType.BYTE; 68 | } 69 | }; 70 | TypeAdapter SHORT_TYPE_ADAPTER = new TypeAdapter() { 71 | @Override 72 | public void write(Short aShort, TProtocol protocol) throws TException { 73 | protocol.writeI16(aShort); 74 | } 75 | 76 | @Override 77 | public Short read(TProtocol protocol) throws TException { 78 | return protocol.readI16(); 79 | } 80 | 81 | @Override 82 | public byte getTType() { 83 | return TType.I16; 84 | } 85 | }; 86 | TypeAdapter INTEGER_TYPE_ADAPTER = new TypeAdapter() { 87 | @Override 88 | public void write(Integer integer, TProtocol protocol) throws TException { 89 | protocol.writeI32(integer); 90 | } 91 | 92 | @Override 93 | public Integer read(TProtocol protocol) throws TException { 94 | return protocol.readI32(); 95 | } 96 | 97 | @Override 98 | public byte getTType() { 99 | return TType.I32; 100 | } 101 | }; 102 | TypeAdapter LONG_TYPE_ADAPTER = new TypeAdapter() { 103 | @Override 104 | public void write(Long aLong, TProtocol protocol) throws TException { 105 | protocol.writeI64(aLong); 106 | } 107 | 108 | @Override 109 | public Long read(TProtocol protocol) throws TException { 110 | return protocol.readI64(); 111 | } 112 | 113 | @Override 114 | public byte getTType() { 115 | return TType.I64; 116 | } 117 | }; 118 | TypeAdapter DOUBLE_TYPE_ADAPTER = new TypeAdapter() { 119 | @Override 120 | public void write(Double aDouble, TProtocol protocol) throws TException { 121 | protocol.writeDouble(aDouble); 122 | } 123 | 124 | @Override 125 | public Double read(TProtocol protocol) throws TException { 126 | return protocol.readDouble(); 127 | } 128 | 129 | @Override 130 | public byte getTType() { 131 | return TType.DOUBLE; 132 | } 133 | }; 134 | TypeAdapter STRING_TYPE_ADAPTER = new TypeAdapter() { 135 | @Override 136 | public void write(String s, TProtocol protocol) throws TException { 137 | protocol.writeString(s); 138 | } 139 | 140 | @Override 141 | public String read(TProtocol protocol) throws TException { 142 | return protocol.readString(); 143 | } 144 | 145 | @Override 146 | public byte getTType() { 147 | return TType.STRING; 148 | } 149 | }; 150 | TypeAdapter BYTE_BUFFER_TYPE_ADAPTER = new TypeAdapter() { 151 | @Override 152 | public void write(ByteBuffer byteBuffer, TProtocol protocol) throws TException { 153 | protocol.writeBinary(byteBuffer); 154 | } 155 | 156 | @Override 157 | public ByteBuffer read(TProtocol protocol) throws TException { 158 | return protocol.readBinary(); 159 | } 160 | 161 | @Override 162 | public byte getTType() { 163 | return TType.STRING; 164 | } 165 | }; 166 | TypeAdapter BYTE_ARRAY_TYPE_ADAPTER = new TypeAdapter() { 167 | @Override 168 | public void write(byte[] bytes, TProtocol protocol) throws TException { 169 | protocol.writeBinary(ByteBuffer.wrap(bytes)); 170 | } 171 | 172 | @Override 173 | public byte[] read(TProtocol protocol) throws TException { 174 | return protocol.readBinary().array(); 175 | } 176 | 177 | @Override 178 | public byte getTType() { 179 | return TType.STRING; 180 | } 181 | }; 182 | } 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Firefly 2 | 3 | ------ 4 | 5 | Lightweight thrift client code generator and runtime library for Android and Java. 6 | 7 | Code generated by firefly has much fewer methods(about 1%) and lines than standard thrift code, which helps applications avoid the notorious 64k limit on methods in Android applications. 8 | 9 | For exmaple, evernote's [public api](https://github.com/evernote/evernote-thrift) is provided with thrift. Methods count of standard thrift generated code: 10 | 11 | ``` 12 | : 20815 13 | com: 20815 14 | evernote: 20815 15 | edam: 20815 16 | error: 226 17 | limits: 2 18 | notestore: 15225 19 | type: 2776 20 | userstore: 2586 21 | Overall method count: 20815 22 | ``` 23 | 24 | Methods count of Firefly generated code: 25 | 26 | ``` 27 | : 204 28 | com: 204 29 | evernote: 204 30 | edam: 204 31 | error: 10 32 | limits: 1 33 | notestore: 90 34 | type: 85 35 | userstore: 18 36 | Overall method count: 204 37 | ``` 38 | 39 | Firefly has an acceptable performance. Local process takes several tens of milliseconds when cold booting a service, which thrift code takes several milliseconds. Hot call takes less than one millisecond almost as fast as thrift code. Anyway, the time taken by local process is ignorable, comparing to the time taken by network. 40 | 41 | This project is inspired by Square's [wire](https://github.com/square/wire) and [retrofit](https://github.com/square/retrofit), and I shamelessly copy a lot of code from Twitter's [scrooge](https://github.com/twitter/scrooge) and Google's [gson](https://github.com/google/gson). 42 | 43 | ## Compiling thrift files 44 | Preparation: 45 | 46 | * clone this repository 47 | * install sbt 48 | 49 | To compile thrift files: 50 | 51 | ```bash 52 | $ cd generator 53 | $ sbt 'run [--output ] [ ...]' 54 | ``` 55 | If you want to generate files with Rx smell or supports Android's `Parcelable`, just put `--rx` or `--android` in the last, like this: 56 | 57 | ```bash 58 | $ cd generator 59 | $ sbt 'run [--output ] [ ...] --rx --android' 60 | ``` 61 | 62 | If you generate codes with `--android`, your project should dependents on `com.meituan.firefly:lib-android` instead of `com.meituan.firefly:library`. 63 | 64 | ### Gradle plugin 65 | If you are using Gradle, you may use the Gradle Plugin: 66 | 67 | ```groovy 68 | buildscript { 69 | repositories { 70 | jcenter() 71 | } 72 | dependencies { 73 | classpath 'com.meituan.firefly:gradle-plugin:0.2.4' 74 | } 75 | } 76 | apply plugin: 'com.meituan.firefly' 77 | ``` 78 | you can change the input diretory and output diretory, like this: 79 | 80 | ```groovy 81 | apply plugin: 'com.meituan.firefly' 82 | apply plugin: 'com.android.library' 83 | firefly { 84 | inputDir file('${project.projectDir}/src/main/idl') 85 | outputDir file('${project.buildDir}/generated/source/firefly') 86 | } 87 | ``` 88 | * inputDir's default value is '${project.projectDir}/src/main/idl' 89 | * outputDir's default value is '${project.buildDir}/generated/source/firefly' 90 | * rxStyle's default value is false 91 | * android's default value is false 92 | 93 | Please notice that, the plugin must be declared before plugin `com.android.library`, `com.android.application` or `java`. 94 | Pay attention to the arguments `rxStyle` and `android`. If you change them, all the output file will be re-generated. 95 | 96 | If you want to generate code with rx smell or support android's Parcelable: 97 | 98 | ```groovy 99 | apply plugin: 'com.meituan.firefly' 100 | apply plugin: 'java' 101 | firefly { 102 | rxStyle true 103 | android true 104 | } 105 | ``` 106 | 107 | The generator assumes that thrift files included by the target thrift file are placed in the same dir of the target thrift file. 108 | 109 | ## Using Firefly in your application 110 | The `library` package contains runtime support libraries that must be included in applications that use Firefly-generated code. 111 | 112 | Include via Maven: 113 | 114 | ```xml 115 | 116 | com.meituan.firefly 117 | library 118 | 0.2.4 119 | 120 | ``` 121 | 122 | or Gradle: 123 | 124 | ```groovy 125 | compile 'com.meituan.firefly:library:0.2.4' 126 | ``` 127 | 128 | or sbt: 129 | 130 | ```scala 131 | libraryDependencies += "com.meituan.firefly" % "library" % "0.2.4" 132 | ``` 133 | 134 | Given the following thrift file for example: 135 | 136 | ``` 137 | namespace java com.meituan.greeting 138 | service GreetingService{ 139 | string greet(string name); 140 | } 141 | ``` 142 | 143 | It generates an interface named `GreetingService`. 144 | 145 | Use of generated code: 146 | 147 | ```Java 148 | final OkHttpClient okhttpClient = new OkHttpClient(); 149 | 150 | GreetingService greetingService = Thrift.instance.create(GreetingService.class, new SimpleProtocolFactory(){ 151 | @Override 152 | public TProtocol get() { 153 | return new TBinaryProtocol(new OkhttpTransport(YOUR_SERVICE_URL, okhttpClient)); 154 | } 155 | }; 156 | 157 | String greeting = greetingService.greet("Han meimei"); 158 | ``` 159 | 160 | 161 | Make sure that `ProtocolFactory.get()` return a new Protocol for each call, or return a same thread-safe Protocol for every call. 162 | 163 | ## Roadmap 164 | 165 | - [x] RxJava support 166 | - [x] Gradle Plugin generates codes automatically 167 | - [x] Serializable support 168 | - [x] Android Parcelable support 169 | 170 | ## License 171 | 172 | ``` 173 | Licensed under the Apache License, Version 2.0 (the "License"); 174 | you may not use this file except in compliance with the License. 175 | You may obtain a copy of the License at 176 | 177 | http://www.apache.org/licenses/LICENSE-2.0 178 | 179 | Unless required by applicable law or agreed to in writing, software 180 | distributed under the License is distributed on an "AS IS" BASIS, 181 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 182 | See the License for the specific language governing permissions and 183 | limitations under the License. 184 | ``` 185 | 186 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/Types.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import java.io.Serializable; 4 | import java.lang.reflect.*; 5 | import java.util.Arrays; 6 | 7 | /** 8 | * Utils for type resolve.
9 | * Copy a lot of code from Gson shamelessly. 10 | */ 11 | public class Types { 12 | public static Class getRawType(Type type) { 13 | if (type instanceof Class) { 14 | // type is a normal class. 15 | return (Class) type; 16 | 17 | } else if (type instanceof ParameterizedType) { 18 | ParameterizedType parameterizedType = (ParameterizedType) type; 19 | 20 | // I'm not exactly sure why getRawType() returns Type instead of Class. 21 | // Neal isn't either but suspects some pathological case related 22 | // to nested classes exists. 23 | Type rawType = parameterizedType.getRawType(); 24 | if (!(rawType instanceof Class)) throw new IllegalArgumentException(); 25 | return (Class) rawType; 26 | 27 | } else if (type instanceof GenericArrayType) { 28 | Type componentType = ((GenericArrayType) type).getGenericComponentType(); 29 | return Array.newInstance(getRawType(componentType), 0).getClass(); 30 | 31 | } else if (type instanceof TypeVariable) { 32 | // we could use the variable's bounds, but that won't work if there are multiple. 33 | // having a raw type that's more general than necessary is okay 34 | return Object.class; 35 | 36 | } else if (type instanceof WildcardType) { 37 | return getRawType(((WildcardType) type).getUpperBounds()[0]); 38 | 39 | } else { 40 | String className = type == null ? "null" : type.getClass().getName(); 41 | throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " 42 | + "GenericArrayType, but <" + type + "> is of type " + className); 43 | } 44 | } 45 | 46 | public static Type canonicalize(Type type) { 47 | if (type instanceof ParameterizedType) { 48 | ParameterizedType p = (ParameterizedType) type; 49 | return new ParameterizedTypeImpl(p.getOwnerType(), 50 | p.getRawType(), p.getActualTypeArguments()); 51 | 52 | } else { 53 | // type is either serializable as-is or unsupported 54 | return type; 55 | } 56 | } 57 | 58 | static boolean equal(Object a, Object b) { 59 | return a == b || (a != null && a.equals(b)); 60 | } 61 | 62 | public static boolean equals(Type a, Type b) { 63 | if (a == b) { 64 | // also handles (a == null && b == null) 65 | return true; 66 | 67 | } else if (a instanceof Class) { 68 | // Class already specifies equals(). 69 | return a.equals(b); 70 | 71 | } else if (a instanceof ParameterizedType) { 72 | if (!(b instanceof ParameterizedType)) { 73 | return false; 74 | } 75 | 76 | // TODO: save a .clone() call 77 | ParameterizedType pa = (ParameterizedType) a; 78 | ParameterizedType pb = (ParameterizedType) b; 79 | return equal(pa.getOwnerType(), pb.getOwnerType()) 80 | && pa.getRawType().equals(pb.getRawType()) 81 | && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); 82 | 83 | } else if (a instanceof GenericArrayType) { 84 | if (!(b instanceof GenericArrayType)) { 85 | return false; 86 | } 87 | 88 | GenericArrayType ga = (GenericArrayType) a; 89 | GenericArrayType gb = (GenericArrayType) b; 90 | return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); 91 | 92 | } else if (a instanceof WildcardType) { 93 | if (!(b instanceof WildcardType)) { 94 | return false; 95 | } 96 | 97 | WildcardType wa = (WildcardType) a; 98 | WildcardType wb = (WildcardType) b; 99 | return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) 100 | && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); 101 | 102 | } else if (a instanceof TypeVariable) { 103 | if (!(b instanceof TypeVariable)) { 104 | return false; 105 | } 106 | TypeVariable va = (TypeVariable) a; 107 | TypeVariable vb = (TypeVariable) b; 108 | return va.getGenericDeclaration() == vb.getGenericDeclaration() 109 | && va.getName().equals(vb.getName()); 110 | 111 | } else { 112 | // This isn't a type we support. Could be a generic array type, wildcard type, etc. 113 | return false; 114 | } 115 | } 116 | 117 | private static int hashCodeOrZero(Object o) { 118 | return o != null ? o.hashCode() : 0; 119 | } 120 | 121 | public static String typeToString(Type type) { 122 | return type instanceof Class ? ((Class) type).getName() : type.toString(); 123 | } 124 | 125 | private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { 126 | private final Type ownerType; 127 | private final Type rawType; 128 | private final Type[] typeArguments; 129 | 130 | public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { 131 | this.ownerType = ownerType == null ? null : canonicalize(ownerType); 132 | this.rawType = canonicalize(rawType); 133 | this.typeArguments = typeArguments.clone(); 134 | for (int t = 0; t < this.typeArguments.length; t++) { 135 | this.typeArguments[t] = canonicalize(this.typeArguments[t]); 136 | } 137 | } 138 | 139 | public Type[] getActualTypeArguments() { 140 | return typeArguments.clone(); 141 | } 142 | 143 | public Type getRawType() { 144 | return rawType; 145 | } 146 | 147 | public Type getOwnerType() { 148 | return ownerType; 149 | } 150 | 151 | @Override 152 | public boolean equals(Object other) { 153 | return other instanceof ParameterizedType 154 | && Types.equals(this, (ParameterizedType) other); 155 | } 156 | 157 | @Override 158 | public int hashCode() { 159 | return Arrays.hashCode(typeArguments) 160 | ^ rawType.hashCode() 161 | ^ hashCodeOrZero(ownerType); 162 | } 163 | 164 | @Override 165 | public String toString() { 166 | StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1)); 167 | stringBuilder.append(typeToString(rawType)); 168 | 169 | if (typeArguments.length == 0) { 170 | return stringBuilder.toString(); 171 | } 172 | 173 | stringBuilder.append("<").append(typeToString(typeArguments[0])); 174 | for (int i = 1; i < typeArguments.length; i++) { 175 | stringBuilder.append(", ").append(typeToString(typeArguments[i])); 176 | } 177 | return stringBuilder.append(">").toString(); 178 | } 179 | 180 | private static final long serialVersionUID = 0; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/adapters/StructTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.adapters; 2 | 3 | import com.meituan.firefly.Thrift; 4 | import com.meituan.firefly.TypeAdapter; 5 | import com.meituan.firefly.Types; 6 | import com.meituan.firefly.annotations.Union; 7 | import org.apache.thrift.TException; 8 | import org.apache.thrift.protocol.*; 9 | 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Modifier; 12 | import java.lang.reflect.Type; 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class StructTypeAdapterFactory implements TypeAdapter.TypeAdapterFactory { 19 | @Override 20 | public TypeAdapter create(Type type, Thrift thrift) { 21 | Class rawType = Types.getRawType(type); 22 | return new StructTypeAdapter(rawType, thrift); 23 | } 24 | 25 | static class StructTypeAdapter implements TypeAdapter { 26 | static class FieldAdapter { 27 | final short id; 28 | final boolean required; 29 | final Field field; 30 | final TypeAdapter adapter; 31 | 32 | public FieldAdapter(short id, boolean required, Field field, TypeAdapter adapter) { 33 | this.id = id; 34 | this.required = required; 35 | this.field = field; 36 | this.adapter = adapter; 37 | } 38 | 39 | TField getTTField() { 40 | return new TField(field.getName(), adapter.getTType(), id); 41 | } 42 | } 43 | 44 | private final Map fieldAdapterMap; 45 | private final List fieldAdapterList; 46 | private final Class rawType; 47 | private final TStruct tStruct; 48 | final boolean isUnion; 49 | 50 | StructTypeAdapter(Class rawType, Thrift thrift) { 51 | this.rawType = rawType; 52 | Field[] fields = rawType.getFields(); 53 | fieldAdapterMap = new HashMap<>(fields.length); 54 | fieldAdapterList = new ArrayList<>(fields.length); 55 | tStruct = new TStruct(rawType.getSimpleName()); 56 | for (Field field : fields) { 57 | if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())) { 58 | continue; 59 | } 60 | com.meituan.firefly.annotations.Field fieldAnnotation = field.getAnnotation(com.meituan.firefly.annotations.Field.class); 61 | if (fieldAnnotation == null) { 62 | throw new IllegalArgumentException("field " + field.getName() + " of struct " + rawType.getSimpleName() + " should be annotated with @Field"); 63 | } 64 | TypeAdapter fieldTypeAdapter = thrift.getAdapter(field.getGenericType()); 65 | FieldAdapter fieldAdapter = new FieldAdapter(fieldAnnotation.id(), fieldAnnotation.required(), field, fieldTypeAdapter); 66 | fieldAdapterMap.put(fieldAnnotation.id(), fieldAdapter); 67 | fieldAdapterList.add(fieldAdapter); 68 | } 69 | isUnion = rawType.getAnnotation(Union.class) != null; 70 | } 71 | 72 | @Override 73 | public void write(Object o, TProtocol protocol) throws TException { 74 | protocol.writeStructBegin(tStruct); 75 | boolean fieldSet = false; 76 | for (FieldAdapter fieldAdapter : fieldAdapterList) { 77 | Object fieldValue = null; 78 | try { 79 | fieldValue = fieldAdapter.field.get(o); 80 | } catch (IllegalAccessException e) { 81 | throw new IllegalStateException(e); 82 | } 83 | if (fieldValue == null) { 84 | if (fieldAdapter.required) { 85 | throw new TProtocolException("Required field '" + fieldAdapter.field.getName() + "' was not present! Struct: " + rawType.getSimpleName()); 86 | } 87 | } else { 88 | if (fieldSet && isUnion) { 89 | throw new TProtocolException("Union with more than one field! Struct: " + rawType.getSimpleName()); 90 | } 91 | protocol.writeFieldBegin(fieldAdapter.getTTField()); 92 | fieldAdapter.adapter.write(fieldValue, protocol); 93 | protocol.writeFieldEnd(); 94 | fieldSet = true; 95 | } 96 | } 97 | protocol.writeFieldStop(); 98 | protocol.writeStructEnd(); 99 | } 100 | 101 | @Override 102 | public Object read(TProtocol protocol) throws TException { 103 | protocol.readStructBegin(); 104 | Object result = null; 105 | try { 106 | result = rawType.newInstance(); 107 | } catch (InstantiationException | IllegalAccessException e) { 108 | throw new IllegalStateException(e); 109 | } 110 | boolean fieldSet = false; 111 | while (true) { 112 | TField tField = protocol.readFieldBegin(); 113 | if (tField.type == TType.STOP) { 114 | break; 115 | } 116 | if (fieldSet && isUnion) { 117 | throw new TProtocolException("Union with more than one field! Struct: " + rawType.getSimpleName()); 118 | } 119 | fieldSet = true; 120 | FieldAdapter fieldAdapter = fieldAdapterMap.get(tField.id); 121 | if (fieldAdapter == null || tField.type != fieldAdapter.adapter.getTType()) { 122 | TProtocolUtil.skip(protocol, tField.type); 123 | } else { 124 | Object fieldValue = fieldAdapter.adapter.read(protocol); 125 | try { 126 | fieldAdapter.field.set(result, fieldValue); 127 | } catch (IllegalAccessException e) { 128 | throw new IllegalStateException(e); 129 | } 130 | } 131 | protocol.readFieldEnd(); 132 | } 133 | protocol.readStructEnd(); 134 | try { 135 | for (FieldAdapter fieldAdapter : fieldAdapterList) { 136 | Object fieldValue = fieldAdapter.field.get(result); 137 | if (fieldValue == null && fieldAdapter.required) { 138 | throw new TProtocolException("Required field '" + fieldAdapter.field.getName() + "' was not present! Struct: " + rawType.getSimpleName()); 139 | } 140 | } 141 | } catch (IllegalAccessException e) { 142 | throw new IllegalStateException(e); 143 | } 144 | if (Thrift.instance.hasDefaultValue()) { 145 | //Set default values 146 | for (Field f : result.getClass().getFields()) { 147 | f.setAccessible(true); 148 | try { 149 | if (f.get(result) == null) { 150 | f.set(result, getDefaultValueForType(f.getType())); 151 | } 152 | } catch (IllegalAccessException e) { 153 | e.printStackTrace(); 154 | } 155 | } 156 | } 157 | return result; 158 | } 159 | 160 | private Object getDefaultValueForType(Class type) { 161 | if (type == Integer.class) { 162 | return Integer.valueOf(0); 163 | } else if (type == Double.class) { 164 | return Double.valueOf(0); 165 | } else if (type == Short.class) { 166 | return Short.valueOf((short) 0); 167 | } else if (type == Long.class) { 168 | return Long.valueOf(0L); 169 | } else if (type == Boolean.class) { 170 | return Boolean.valueOf(false); 171 | } 172 | return null; 173 | } 174 | 175 | @Override 176 | public byte getTType() { 177 | return TType.STRUCT; 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /generator/src/main/scala-2.11/com/meituan/firefly/ThriftParser.scala: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly 2 | 3 | import java.io.{File, FileNotFoundException} 4 | 5 | import com.meituan.firefly.node._ 6 | import jdk.nashorn.internal.runtime.ParserException 7 | 8 | import scala.io.Source 9 | import scala.util.parsing.combinator._ 10 | 11 | class ThriftParser(dir: File) extends RegexParsers { 12 | assert(dir == null || dir.isDirectory) 13 | // 1 2 3 4 4a 4b 4c 4d 14 | override val whiteSpace = """(\s+|(//.*\r?\n)|(#([^@\r\n].*)?\r?\n)|(/\*[^\*]([^\*]+|\r?\n|\*(?!/))*\*/))+""".r 15 | // 1: whitespace, 1 or more 16 | // 2: leading // followed by anything 0 or more, until newline 17 | // 3: leading # then NOT a @ followed by anything 0 or more, until newline 18 | // 4: leading /* then NOT a *, then... 19 | // 4a: not a *, 1 or more times 20 | // 4b: OR a newline 21 | // 4c: OR a * followed by a 0-width lookahead / (not sure why we have this -KO) 22 | // (0 or more of 4b/4c/4d) 23 | // 4d: ending */ 24 | 25 | 26 | //Document 27 | lazy val document: Parser[Document] = rep(header) ~ rep(definition) <~ opt(comments) ^^ { 28 | case headers ~ defs => Document(headers, defs) 29 | } 30 | 31 | //Header 32 | lazy val header: Parser[Header] = include | namespace 33 | 34 | lazy val include: Parser[Include] = opt(comments) ~> "include" ~> literal ^^ { 35 | s => Include(s.value, parseFile(new File(dir, s.value))) 36 | } 37 | 38 | lazy val namespace: Parser[NameSpace] = opt(comments) ~> "namespace" ~> namespaceScope ~ identifier ^^ { 39 | case scope ~ id => NameSpace(scope, id) 40 | } 41 | lazy val namespaceScope = "*" | "cpp" | "java" | "py" | "perl" | "rb" | "cocoa" | "csharp" | "php" | "as3" 42 | 43 | //Definition 44 | lazy val definition: Parser[Definition] = const | typedef | enum | struct | union | exception | service 45 | 46 | lazy val const: Parser[Const] = opt(comments) ~ ("const" ~> fieldType) ~ simpleId ~ ("=" ~> constValue) <~ opt(listSeparator) ^^ { 47 | case cm ~ ft ~ name ~ value => Const(ft, name, value, cm) 48 | } 49 | lazy val typedef: Parser[Typedef] = (opt(comments) <~ "typedef") ~ definitionType ~ simpleId ^^ { 50 | case cm ~ dt ~ name => Typedef(dt, name, cm) 51 | } 52 | lazy val enum: Parser[Enum] = (opt(comments) <~ "enum") ~ simpleId ~ ("{" ~> rep(simpleId ~ opt("=" ~> intConstant) <~ opt(listSeparator)) <~ "}") ^^ { 53 | case cm ~ name ~ elems => { 54 | val valuedElems = (elems.filter(_._2.isEmpty).zipWithIndex.map(t => (t._1._1, t._2)) ++ elems.filter(_._2.isDefined).map(t => (t._1, t._2.get.value.toInt))).sortBy(_._2) 55 | findDuplicate(valuedElems)(_._2).foreach(e => throw new RepeatingEnumValueException(name.name, e._2)) 56 | Enum(name, valuedElems, cm) 57 | } 58 | } 59 | lazy val struct: Parser[Struct] = structlike("struct") ^^ { 60 | case cm ~ id ~ fields => { 61 | checkFieldIds(fields) 62 | Struct(id, fields, cm) 63 | } 64 | } 65 | lazy val union: Parser[Union] = structlike("union") ^^ { 66 | case cm ~ name ~ fields => { 67 | fields.find(_.requiredness.isDefined).foreach(f => throw UnionFieldRequirednessException(name.name, f.identifier.name, f.requiredness.get.toString)) 68 | checkFieldIds(fields) 69 | Union(name, fields, cm) 70 | } 71 | } 72 | lazy val exception: Parser[ExceptionDef] = structlike("exception") ^^ { 73 | case cm ~ name ~ fields => { 74 | checkFieldIds(fields) 75 | ExceptionDef(name, fields, cm) 76 | } 77 | } 78 | lazy val service: Parser[Service] = opt(comments) ~ ("service" ~> simpleId) ~ opt("extends" ~> identifier) ~ ("{" ~> rep(function) <~ "}") ^^ { 79 | case cm ~ name ~ parent ~ functions => Service(name, parent, functions, cm) 80 | } 81 | 82 | def structlike(keyword: String) = opt(comments) ~ (keyword ~> simpleId) ~ ("{" ~> rep(field) <~ "}") 83 | 84 | def findDuplicate[A, B](l: List[A])(f: A => B): Option[A] = { 85 | def findDuplicate[A, B](l: List[A], s: Set[B], f: A => B): Option[A] = l match { 86 | case Nil => None 87 | case (h :: t) => 88 | val b = f(h) 89 | if (s(b)) Some(h) else findDuplicate(t, s + b, f) 90 | } 91 | findDuplicate(l, Set(), f) 92 | } 93 | 94 | def checkFieldIds(fields: List[Field]) = { 95 | fields.find(field => field.id.isDefined && field.id.get < 0).foreach(field => throw new NegativeFieldIdException(field.identifier.name)) 96 | findDuplicate(fields.filter(_.id.isDefined))(_.id).foreach(field => throw new DuplicateFieldIdException(field.identifier.name)) 97 | } 98 | 99 | //Field 100 | lazy val field = opt(comments) ~ opt(intConstant <~ ":") ~ opt(requiredness) ~ fieldType ~ simpleId ~ opt("=" ~> constValue) <~ opt(listSeparator) ^^ { 101 | case cm ~ id ~ req ~ ft ~ name ~ value => Field(id.map(_.value.toInt), req, ft, name, value, cm) 102 | } 103 | 104 | lazy val requiredness: Parser[Requiredness] = ("required" | "optional") ^^ { 105 | case "required" => Requiredness.Required 106 | case _ => Requiredness.Optional 107 | } 108 | 109 | //Functions 110 | lazy val function = opt(comments) ~ opt("oneway") ~ functionType ~ simpleId ~ ("(" ~> rep(field) <~ ")") ~ opt(throws) <~ opt(listSeparator) ^^ { 111 | case cm ~ oneway ~ funcType ~ name ~ params ~ throws => 112 | checkFieldIds(params) 113 | checkFieldIds(throws.getOrElse(List())) 114 | Function(if (oneway.isDefined) OnewayVoid else funcType, name, params, throws, cm) 115 | } 116 | lazy val functionType = "void" ^^^ Void | fieldType 117 | lazy val throws = "throws" ~> "(" ~> rep(field) <~ ")" 118 | //Types 119 | lazy val fieldType: Parser[Type] = baseType | containerType | identifierType 120 | 121 | lazy val definitionType = baseType | containerType 122 | 123 | lazy val baseType = { 124 | "bool" ^^^ TBool | 125 | "byte" ^^^ TByte | 126 | "i16" ^^^ TI16 | 127 | "i32" ^^^ TI32 | 128 | "i64" ^^^ TI64 | 129 | "double" ^^^ TDouble | 130 | "string" ^^^ TString | 131 | "binary" ^^^ TBinary 132 | } 133 | lazy val identifierType = identifier ^^ { case id => IdentifierType(id) } 134 | lazy val containerType = mapType | setType | listType 135 | lazy val mapType = "map" ~> "<" ~> ((fieldType <~ ",") ~ (fieldType <~ ">")) ^^ { 136 | case keyType ~ valueType => MapType(keyType, valueType) 137 | } 138 | lazy val setType = "set" ~> "<" ~> fieldType <~ ">" ^^ { case tp => SetType(tp) } 139 | lazy val listType = "list" ~> "<" ~> fieldType <~ ">" ^^ { case tp => ListType(tp) } 140 | //Constant Values 141 | lazy val constValue = numberConstant | boolConstant | literal | constIdentifier | constList | constMap 142 | 143 | 144 | lazy val intConstant = """[-+]?\d+(?!\.)""".r ^^ { x => IntConstant(x.toLong) } 145 | 146 | lazy val numberConstant = """[+-]?\d+(\.\d+)?([Ee]\d+)?""".r ^^ { 147 | case x => 148 | if (x exists ("eE." contains _)) DoubleConstant(x.toDouble) 149 | else IntConstant(x.toLong) 150 | } 151 | 152 | lazy val boolConstant = { 153 | "true" ^^^ BoolConstant(true) | 154 | "false" ^^^ BoolConstant(false) 155 | } 156 | 157 | lazy val constIdentifier = identifier ^^ { case id => IdConstant(id) } 158 | 159 | lazy val constList: Parser[ConstList] = "[" ~> repsep(constValue, listSeparator) <~ opt(listSeparator) <~ "]" ^^ { 160 | case list => ConstList(list) 161 | } 162 | lazy val keyvalue: Parser[(ConstValue, ConstValue)] = constValue ~ (":" ~> constValue) ^^ { 163 | case k ~ v => k -> v 164 | } 165 | lazy val constMap: Parser[ConstMap] = "{" ~> repsep(keyvalue, listSeparator) <~ opt(listSeparator) <~ "}" ^^ { 166 | case elems => ConstMap(elems) 167 | } 168 | 169 | //Basic Definitions 170 | 171 | // use a single regex to match string quote-to-quote, so that whitespace parser doesn"t 172 | // get executed inside the quotes 173 | lazy val doubleQuotedString = """(")(\.|[^\"])*(")""".r 174 | lazy val singleQuotedString = """'(\\.|[^\\'])*'""".r 175 | 176 | lazy val literal = (doubleQuotedString | singleQuotedString) ^^ { 177 | // strip off quotes 178 | x => Literal(x.substring(1, x.length - 1)) 179 | } 180 | 181 | val identifierRegex = """[A-Za-z_][A-Za-z0-9\._]*""".r 182 | lazy val identifier = identifierRegex ^^ { 183 | x => Identifier(x) 184 | } 185 | 186 | private[this] val thriftKeywords = Set[String]( 187 | "async", 188 | "const", 189 | "enum", 190 | "exception", 191 | "extends", 192 | "include", 193 | "namespace", 194 | "optional", 195 | "required", 196 | "service", 197 | "struct", 198 | "throws", 199 | "typedef", 200 | "union", 201 | "void", 202 | // Built-in types are also keywords. 203 | "binary", 204 | "bool", 205 | "byte", 206 | "double", 207 | "i16", 208 | "i32", 209 | "i64", 210 | "list", 211 | "map", 212 | "set", 213 | "string" 214 | ) 215 | 216 | lazy val simpleIdRegex = "[A-Za-z_][A-Za-z0-9_]*".r 217 | lazy val simpleId = simpleIdRegex ^^ { x => 218 | if (thriftKeywords.contains(x)) 219 | throw new KeywordException(x) 220 | 221 | SimpleId(x) 222 | } 223 | lazy val listSeparator = "[,|;]".r 224 | 225 | /** 226 | * Matches scaladoc/javadoc style comments. 227 | */ 228 | lazy val comments: Parser[String] = { 229 | rep1( """(?s)/\*\*.+?\*/""".r) ^^ { 230 | case cs => 231 | cs.mkString("\n") 232 | } 233 | } 234 | 235 | def parseFile(file: File): Document = { 236 | if (!file.canRead) 237 | throw new FileNotFoundException(file.getCanonicalPath + " not Found") 238 | parseAll(document, Source.fromFile(file).mkString) match { 239 | case Success(result, _) => result 240 | case x@Failure(_, _) => throw new ParseException(x toString) 241 | case x@Error(_, _) => throw new ParserException(x toString) 242 | } 243 | } 244 | } 245 | 246 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/FunctionCall.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import com.meituan.firefly.annotations.Field; 4 | import com.meituan.firefly.annotations.Func; 5 | import org.apache.thrift.TApplicationException; 6 | import org.apache.thrift.TException; 7 | import org.apache.thrift.protocol.*; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.ParameterizedType; 12 | import java.lang.reflect.Type; 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | 17 | import rx.Observable; 18 | import rx.Scheduler; 19 | import rx.Subscriber; 20 | 21 | /** 22 | * Executes real function call. 23 | */ 24 | class FunctionCall { 25 | private final List requestTypeList = new ArrayList<>(); 26 | private final HashMap responseExceptionTypeMap = new HashMap<>(); 27 | private FieldSpec responseSuccessType; 28 | private final String methodName; 29 | private final boolean oneway; 30 | private final TStruct argsStruct; 31 | private final boolean isObservable; 32 | private boolean isVoid = false; 33 | 34 | FunctionCall(Method method, Thrift thrift) { 35 | methodName = method.getName(); 36 | Func func = method.getAnnotation(Func.class); 37 | if (func == null) { 38 | throw new IllegalArgumentException("method " + methodName + " should be annotated with @Func"); 39 | } 40 | oneway = func.oneway(); 41 | isObservable = getIsObservable(method); 42 | parseRequest(method, thrift); 43 | parseResponse(method, func, thrift); 44 | argsStruct = new TStruct(methodName + "_args"); 45 | } 46 | 47 | void parseRequest(Method method, Thrift thrift) { 48 | Annotation[][] parametersAnnotations = method.getParameterAnnotations(); 49 | Type[] parameterTypes = method.getGenericParameterTypes(); 50 | for (int i = 0, n = parametersAnnotations.length; i < n; i++) { 51 | Field paramField = null; 52 | Annotation[] parameterAnnotations = parametersAnnotations[i]; 53 | for (Annotation annotation : parameterAnnotations) { 54 | if (annotation instanceof Field) { 55 | paramField = (Field) annotation; 56 | break; 57 | } 58 | } 59 | if (paramField == null) { 60 | throw new IllegalArgumentException("parameter" + " of method " + methodName + " is not annotated with @Field"); 61 | } 62 | TypeAdapter typeAdapter = thrift.getAdapter(parameterTypes[i]); 63 | requestTypeList.add(new FieldSpec(paramField.id(), paramField.required(), paramField.name(), typeAdapter)); 64 | } 65 | } 66 | 67 | void parseResponse(Method method, Func func, Thrift thrift) { 68 | Type type = getMethodReturnType(method); 69 | //if return type is Void,we don't need to find adapter 70 | if (Void.class.equals(type) || void.class.equals(type)) { 71 | isVoid = true; 72 | } else { 73 | TypeAdapter returnTypeAdapter = thrift.getAdapter(type); 74 | responseSuccessType = new FieldSpec((short) 0, false, "success", returnTypeAdapter); //success 75 | } 76 | Field[] exceptionFields = func.value(); 77 | Class[] exceptions = method.getExceptionTypes(); 78 | if (exceptionFields != null) { 79 | for (int i = 0, n = exceptionFields.length; i < n; i++) { 80 | Field exceptionField = exceptionFields[i]; 81 | TypeAdapter exceptionTypeAdapter = thrift.getAdapter(exceptions[i]); 82 | responseExceptionTypeMap.put(exceptionField.id(), new FieldSpec(exceptionField.id(), false, exceptionField.name(), exceptionTypeAdapter)); 83 | } 84 | } 85 | } 86 | 87 | private Type getMethodReturnType(Method method) { 88 | Type type = method.getGenericReturnType(); 89 | if (isObservable) { 90 | if (type instanceof ParameterizedType) { 91 | type = ((ParameterizedType) type).getActualTypeArguments()[0]; 92 | } else { 93 | type = Object.class; 94 | } 95 | } 96 | return type; 97 | } 98 | 99 | 100 | Object apply(Object[] args, TProtocol protocol, int seqid) throws Exception { 101 | return apply(args, protocol, seqid, null); 102 | } 103 | 104 | Object apply(final Object[] args, final TProtocol protocol, final int seqid, Scheduler subscribScheduler) throws Exception { 105 | if (isObservable) { 106 | Observable observable = Observable.create(new Observable.OnSubscribe() { 107 | @Override 108 | public void call(Subscriber subscriber) { 109 | try { 110 | if (subscriber.isUnsubscribed()) { 111 | return; 112 | } 113 | subscriber.onNext(FunctionCall.this.sendAndRecv(args, protocol, seqid)); 114 | subscriber.onCompleted(); 115 | } catch (Exception e) { 116 | subscriber.onError(e); 117 | } 118 | } 119 | }); 120 | if (null != subscribScheduler) 121 | return observable.subscribeOn(subscribScheduler); 122 | else 123 | return observable; 124 | } 125 | return sendAndRecv(args, protocol, seqid); 126 | } 127 | 128 | Object sendAndRecv(Object[] args, TProtocol protocol, int seqid) throws Exception { 129 | send(args, protocol, seqid); 130 | if (!oneway) { 131 | return recv(protocol, seqid); 132 | } 133 | return null; 134 | } 135 | 136 | void send(Object[] args, TProtocol protocol, int seqid) throws TException { 137 | protocol.writeMessageBegin(new TMessage(methodName, TMessageType.CALL, seqid)); 138 | protocol.writeStructBegin(argsStruct); 139 | //method with no parameters 140 | if (null != args) { 141 | for (int i = 0, n = args.length; i < n; i++) { 142 | FieldSpec fieldSpec = requestTypeList.get(i); 143 | Object value = args[i]; 144 | if (value == null) { 145 | if (fieldSpec.required) { 146 | throw new TProtocolException("Required field '" + fieldSpec.name + "' was not present! Struct: " + argsStruct.name); 147 | } 148 | } else { 149 | protocol.writeFieldBegin(fieldSpec.tField); 150 | fieldSpec.typeAdapter.write(value, protocol); 151 | protocol.writeFieldEnd(); 152 | } 153 | } 154 | } 155 | protocol.writeFieldStop(); 156 | protocol.writeStructEnd(); 157 | protocol.writeMessageEnd(); 158 | protocol.getTransport().flush(); 159 | } 160 | 161 | Object recv(TProtocol protocol, int seqid) throws Exception { 162 | TMessage msg = protocol.readMessageBegin(); 163 | if (msg.type == TMessageType.EXCEPTION) { 164 | TApplicationException applicationException = TApplicationException.read(protocol); 165 | protocol.readMessageEnd(); 166 | throw applicationException; 167 | } 168 | if (msg.seqid != seqid) { 169 | throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID, methodName + " failed: out of sequence response"); 170 | } 171 | protocol.readStructBegin(); 172 | Object success = null; 173 | Exception exception = null; 174 | while (true) { 175 | TField tField = protocol.readFieldBegin(); 176 | if (tField.type == TType.STOP) { 177 | break; 178 | } 179 | FieldSpec fieldSpec = null; 180 | if (tField.id == 0) { 181 | fieldSpec = responseSuccessType; 182 | } else { 183 | fieldSpec = responseExceptionTypeMap.get(tField.id); 184 | } 185 | if (fieldSpec == null || fieldSpec.typeAdapter.getTType() != tField.type) { 186 | TProtocolUtil.skip(protocol, tField.type); 187 | } else { 188 | Object value = fieldSpec.typeAdapter.read(protocol); 189 | if (tField.id == 0) { 190 | success = value; 191 | } else { 192 | exception = (Exception) value; 193 | } 194 | } 195 | protocol.readFieldEnd(); 196 | } 197 | protocol.readStructEnd(); 198 | protocol.readMessageEnd(); 199 | if (exception != null) { 200 | throw exception; 201 | } 202 | if (success != null) { 203 | return success; 204 | } 205 | if (isVoid) { 206 | return null; 207 | } 208 | throw new TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, methodName + " failed: unknown result"); 209 | } 210 | 211 | public boolean getIsObservable(Method method) { 212 | return Observable.class.isAssignableFrom(Types.getRawType(method.getGenericReturnType())); 213 | } 214 | 215 | static class FieldSpec { 216 | final short id; 217 | final boolean required; 218 | final String name; 219 | final TypeAdapter typeAdapter; 220 | final TField tField; 221 | 222 | public FieldSpec(short id, boolean required, String name, TypeAdapter typeAdapter) { 223 | this.id = id; 224 | this.required = required; 225 | this.name = name; 226 | this.typeAdapter = typeAdapter; 227 | this.tField = new TField(name, typeAdapter.getTType(), id); 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /library/src/main/java/com/meituan/firefly/Thrift.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly; 2 | 3 | import com.meituan.firefly.adapters.*; 4 | import org.apache.thrift.TException; 5 | import org.apache.thrift.protocol.TProtocol; 6 | import rx.Scheduler; 7 | 8 | import java.lang.reflect.InvocationHandler; 9 | import java.lang.reflect.Method; 10 | import java.lang.reflect.Proxy; 11 | import java.lang.reflect.Type; 12 | import java.nio.ByteBuffer; 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | import static com.meituan.firefly.TypeAdapter.*; 19 | 20 | /** 21 | * {@link Thrift} is the facade of Firefly library, provides static method for creating 22 | * dynamic thrift client instances of services. 23 | *

For example:

24 | *
 25 |  *     service ExampleService{
 26 |  *         string hello(string who);
 27 |  *     }
 28 |  * 
29 | * The thrift idl mentioned, will generate a interface ExampleService can be used like: 30 | *
 31 |  *     Thrift thrift = new Thrift();
 32 |  *     ProtocolFactory factory = new SimpleProtocolFactory(){
 33 |  *         public TProtocol get(){
 34 |  *             TTransport transport = new OkhttpTransport("http://api.example.com", new OkhttpClient());
 35 |  *             return new TBinaryProtocol(transport);
 36 |  *         }
 37 |  *     }
 38 |  *     ExampleService exampleClient = thrift.create(ExampleService.class, factory);
 39 |  *     exampleClient.hello("Sam");
 40 |  * 
41 | * When calling exampleClient's hello method, firefly will make a rpc call to the remote service deployed on host 42 | * api.example.com using http, then return the remote service's result (maybe "hello sam!") as method result. 43 | */ 44 | public final class Thrift { 45 | public static final Thrift instance = new Thrift(); 46 | private boolean hasDefaultValue = false; 47 | private final Map functionCallMap = new HashMap<>(); 48 | private final Map typeAdapterMap = new HashMap<>(); 49 | private final List typeAdapterFactories = new ArrayList<>(); 50 | private final ThreadLocal>> calls = new ThreadLocal<>(); 51 | 52 | private Thrift() { 53 | typeAdapterMap.put(Boolean.class, BOOLEAN_TYPE_ADAPTER); 54 | typeAdapterMap.put(Byte.class, BYTE_TYPE_ADAPTER); 55 | typeAdapterMap.put(Short.class, SHORT_TYPE_ADAPTER); 56 | typeAdapterMap.put(Integer.class, INTEGER_TYPE_ADAPTER); 57 | typeAdapterMap.put(Long.class, LONG_TYPE_ADAPTER); 58 | typeAdapterMap.put(Double.class, DOUBLE_TYPE_ADAPTER); 59 | typeAdapterMap.put(String.class, STRING_TYPE_ADAPTER); 60 | typeAdapterMap.put(ByteBuffer.class, BYTE_BUFFER_TYPE_ADAPTER); 61 | typeAdapterMap.put(byte[].class, BYTE_ARRAY_TYPE_ADAPTER); 62 | 63 | typeAdapterFactories.add(new SetTypeAdapterFactory()); 64 | typeAdapterFactories.add(new ListTypeAdapterFactory()); 65 | typeAdapterFactories.add(new MapTypeAdapterFactory()); 66 | typeAdapterFactories.add(new EnumTypeAdapterFactory()); 67 | typeAdapterFactories.add(new StructTypeAdapterFactory()); 68 | } 69 | 70 | public void setDefaultValue(boolean defaultValue) { 71 | this.hasDefaultValue = defaultValue; 72 | } 73 | 74 | public boolean hasDefaultValue() { 75 | return hasDefaultValue; 76 | } 77 | 78 | public interface TProtocolFactory { 79 | TProtocol get(Method method, Object[] args); 80 | } 81 | 82 | public static abstract class SimpleTProtocolFactory implements TProtocolFactory { 83 | @Override 84 | public TProtocol get(Method method, Object[] args) { 85 | return get(); 86 | } 87 | 88 | public abstract TProtocol get(); 89 | } 90 | 91 | public TypeAdapter getAdapter(Type type) { 92 | //void/Void check 93 | if (Void.class.equals(type) || void.class.equals(type)) { 94 | return null; 95 | } 96 | final Type canonicalizeType = Types.canonicalize(type); 97 | TypeAdapter typeAdapter = typeAdapterMap.get(canonicalizeType); 98 | if (typeAdapter == null) { 99 | synchronized (typeAdapterMap) { 100 | typeAdapter = typeAdapterMap.get(canonicalizeType); 101 | if (typeAdapter == null) { 102 | Map> threadCalls = calls.get(); 103 | boolean requiresThreadLocalCleanup = false; 104 | if (threadCalls == null) { 105 | threadCalls = new HashMap<>(); 106 | calls.set(threadCalls); 107 | requiresThreadLocalCleanup = true; 108 | } 109 | FutureTypeAdapter ongoingCall = threadCalls.get(canonicalizeType); 110 | if (ongoingCall != null) { 111 | return ongoingCall; 112 | } 113 | 114 | try { 115 | FutureTypeAdapter call = new FutureTypeAdapter(); 116 | threadCalls.put(canonicalizeType, call); 117 | 118 | typeAdapter = createTypeAdapter(canonicalizeType); 119 | call.setDelegate(typeAdapter); 120 | typeAdapterMap.put(canonicalizeType, typeAdapter); 121 | } finally { 122 | threadCalls.remove(canonicalizeType); 123 | if (requiresThreadLocalCleanup) { 124 | calls.remove(); 125 | } 126 | } 127 | } 128 | } 129 | } 130 | return typeAdapter; 131 | } 132 | 133 | TypeAdapter createTypeAdapter(Type type) { 134 | for (TypeAdapterFactory factory : typeAdapterFactories) { 135 | TypeAdapter typeAdapter = factory.create(type, this); 136 | if (typeAdapter != null) { 137 | return typeAdapter; 138 | } 139 | } 140 | throw new IllegalArgumentException("Unsupport Type : " + type.toString()); 141 | } 142 | 143 | private FunctionCall getFunctionCall(Method method) { 144 | FunctionCall functionCall = functionCallMap.get(method); 145 | if (functionCall == null) { 146 | synchronized (functionCallMap) { 147 | functionCall = functionCallMap.get(method); 148 | if (functionCall == null) { 149 | functionCall = createFunctionCall(method); 150 | functionCallMap.put(method, functionCall); 151 | } 152 | } 153 | } 154 | return functionCall; 155 | } 156 | 157 | private FunctionCall createFunctionCall(Method method) { 158 | return new FunctionCall(method, this); 159 | } 160 | 161 | private static class Chain implements Processor { 162 | private final Processor pre; 163 | private final Interceptor interceptor; 164 | 165 | public Chain(Processor pre, Interceptor interceptor) { 166 | this.pre = pre; 167 | this.interceptor = interceptor; 168 | } 169 | 170 | @Override 171 | public Object process(Method method, Object[] args, TProtocol protocol, int seqId) throws Throwable { 172 | return interceptor.intercept(method, args, protocol, seqId, pre); 173 | } 174 | } 175 | 176 | private class Client implements InvocationHandler { 177 | private final TProtocolFactory protocolFactory; 178 | private int seqid; 179 | private final Processor processor; 180 | 181 | 182 | public Client(TProtocolFactory protocolFactory, Interceptor[] interceptors) { 183 | this(protocolFactory, null, interceptors); 184 | } 185 | 186 | public Client(TProtocolFactory protocolFactory, final Scheduler subscribScheduler, Interceptor[] interceptors) { 187 | this.protocolFactory = protocolFactory; 188 | Processor processor = new Processor() { 189 | @Override 190 | public Object process(Method method, Object[] args, TProtocol protocol, int seqId) throws Throwable { 191 | return Thrift.this.getFunctionCall(method).apply(args, protocol, seqId, subscribScheduler); 192 | } 193 | }; 194 | if (interceptors != null && interceptors.length > 0) { 195 | for (Interceptor interceptor : interceptors) { 196 | processor = new Chain(processor, interceptor); 197 | } 198 | } 199 | this.processor = processor; 200 | } 201 | 202 | @Override 203 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 204 | if (method.getDeclaringClass() == Object.class) { 205 | return method.invoke(this, args); 206 | } 207 | return processor.process(method, args, protocolFactory.get(method, args), ++seqid); 208 | } 209 | } 210 | 211 | /** 212 | * Create a dynamic proxy of a specified interface that generated by firefly generator from a service in thrift's idl. 213 | * 214 | * @param service a interface generated from a service in thrift's idl 215 | * @param protocolFactory a factory return protocols used in every method call 216 | * @param interceptors interceptors works as a chain, interceptor in front will be executed downstream 217 | * @return an instance implements the service interface 218 | */ 219 | public T create(Class service, TProtocolFactory protocolFactory, Interceptor... interceptors) { 220 | 221 | return create(service, protocolFactory, null, interceptors); 222 | } 223 | 224 | /** 225 | * Create a dynamic proxy of a specified interface that generated by firefly generator from a service in thrift's idl. 226 | * 227 | * @param service a interface generated from a service in thrift's idl 228 | * @param protocolFactory a factory return protocols used in every method call 229 | * @param subscribScheduler a scheduler on which method call will be run,recommend create a new scheduler in the a backgroud thread 230 | * @param interceptors interceptors works as a chain, interceptor in front will be executed downstream 231 | * @return an instance implements the service interface 232 | */ 233 | public T create(Class service, TProtocolFactory protocolFactory, Scheduler subscribScheduler, Interceptor... interceptors) { 234 | if (!service.isInterface()) { 235 | throw new IllegalArgumentException("service should be interface"); 236 | } 237 | return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service}, new Client(protocolFactory, subscribScheduler, interceptors)); 238 | } 239 | 240 | static class FutureTypeAdapter implements TypeAdapter { 241 | TypeAdapter delegate; 242 | 243 | void setDelegate(TypeAdapter delegate) { 244 | this.delegate = delegate; 245 | } 246 | 247 | @Override 248 | public void write(T t, TProtocol protocol) throws TException { 249 | if (delegate == null) { 250 | throw new IllegalStateException(); 251 | } 252 | delegate.write(t, protocol); 253 | } 254 | 255 | @Override 256 | public T read(TProtocol protocol) throws TException { 257 | if (delegate == null) { 258 | throw new IllegalStateException(); 259 | } 260 | return delegate.read(protocol); 261 | } 262 | 263 | @Override 264 | public byte getTType() { 265 | if (delegate == null) { 266 | throw new IllegalStateException(); 267 | } 268 | return delegate.getTType(); 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/adapters/StructTypeAdapterTest.java: -------------------------------------------------------------------------------- 1 | package com.meituan.firefly.adapters; 2 | 3 | import com.meituan.firefly.Thrift; 4 | import com.meituan.firefly.testfirefly.*; 5 | import org.apache.thrift.protocol.TBinaryProtocol; 6 | import org.apache.thrift.transport.TMemoryBuffer; 7 | import org.assertj.core.api.Condition; 8 | import org.junit.Test; 9 | 10 | import java.util.Arrays; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Map; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | 17 | public class StructTypeAdapterTest { 18 | Thrift thrift = Thrift.instance; 19 | 20 | @Test 21 | public void shouldReadStruct_thatThriftWrite() throws Exception { 22 | TMemoryBuffer transport = new TMemoryBuffer(1024); 23 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 24 | com.meituan.firefly.testthrift.OrderedStruct thriftOrderedStruct = new com.meituan.firefly.testthrift.OrderedStruct(99); 25 | thriftOrderedStruct.write(protocol); 26 | 27 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(OrderedStruct.class, thrift); 28 | OrderedStruct fireflyOrderedStruct = (OrderedStruct) structTypeAdapter.read(protocol); 29 | assertThat(fireflyOrderedStruct).isNotNull(); 30 | assertThat(fireflyOrderedStruct.id).isEqualTo(99); 31 | } 32 | 33 | @Test 34 | public void shouldWriteStruct_thatThriftRead() throws Exception { 35 | TMemoryBuffer transport = new TMemoryBuffer(1024); 36 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 37 | OrderedStruct fireflyOrderedStruct = new OrderedStruct(); 38 | fireflyOrderedStruct.id = 99; 39 | 40 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(OrderedStruct.class, thrift); 41 | structTypeAdapter.write(fireflyOrderedStruct, protocol); 42 | com.meituan.firefly.testthrift.OrderedStruct thriftOrderedStruct = new com.meituan.firefly.testthrift.OrderedStruct(); 43 | thriftOrderedStruct.read(protocol); 44 | assertThat(thriftOrderedStruct.id).isEqualTo(99); 45 | } 46 | 47 | @Test 48 | public void shouldReadMixStruct_thatThriftWrite() throws Exception { 49 | TMemoryBuffer transport = new TMemoryBuffer(1024); 50 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 51 | com.meituan.firefly.testthrift.MixStruct thriftMixStruct = new com.meituan.firefly.testthrift.MixStruct(1, 2); 52 | thriftMixStruct.write(protocol); 53 | 54 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(MixStruct.class, thrift); 55 | MixStruct fireflyMixStruct = (MixStruct) structTypeAdapter.read(protocol); 56 | assertThat(fireflyMixStruct).isNotNull(); 57 | assertThat(fireflyMixStruct.id).isEqualTo(1); 58 | assertThat(fireflyMixStruct.uid).isEqualTo(2); 59 | } 60 | 61 | @Test 62 | public void shouldWriteMixStruct_thatThriftRead() throws Exception { 63 | TMemoryBuffer transport = new TMemoryBuffer(1024); 64 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 65 | MixStruct fireflyMixStruct = new MixStruct(); 66 | fireflyMixStruct.id = 1; 67 | fireflyMixStruct.uid = 2; 68 | 69 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(MixStruct.class, thrift); 70 | structTypeAdapter.write(fireflyMixStruct, protocol); 71 | com.meituan.firefly.testthrift.MixStruct thriftMixStruct = new com.meituan.firefly.testthrift.MixStruct(); 72 | thriftMixStruct.read(protocol); 73 | assertThat(thriftMixStruct.id).isEqualTo(1); 74 | assertThat(thriftMixStruct.uid).isEqualTo(2); 75 | } 76 | 77 | @Test 78 | public void shouldReadUnion_thatThriftWrite() throws Exception { 79 | TMemoryBuffer transport = new TMemoryBuffer(1024); 80 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 81 | com.meituan.firefly.testthrift.OrderedStruct thriftOrderedStruct = new com.meituan.firefly.testthrift.OrderedStruct(99); 82 | com.meituan.firefly.testthrift.UnionB thriftUnionB = new com.meituan.firefly.testthrift.UnionB(com.meituan.firefly.testthrift.UnionB._Fields.OS, thriftOrderedStruct); 83 | thriftUnionB.write(protocol); 84 | 85 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(UnionB.class, thrift); 86 | UnionB fireflyUnionB = (UnionB) structTypeAdapter.read(protocol); 87 | assertThat(fireflyUnionB).isNotNull(); 88 | assertThat(fireflyUnionB.os).isNotNull(); 89 | assertThat(fireflyUnionB.os.id).isEqualTo(99); 90 | } 91 | 92 | @Test 93 | public void shouldWriteUnion_thatThriftRead() throws Exception { 94 | TMemoryBuffer transport = new TMemoryBuffer(1024); 95 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 96 | UnionB fireflyUnionB = new UnionB(); 97 | OrderedStruct fireflyOrderedStruct = new OrderedStruct(); 98 | fireflyOrderedStruct.id = 99; 99 | fireflyUnionB.os = fireflyOrderedStruct; 100 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(UnionB.class, thrift); 101 | structTypeAdapter.write(fireflyUnionB, protocol); 102 | 103 | com.meituan.firefly.testthrift.UnionB thriftUnionB = new com.meituan.firefly.testthrift.UnionB(); 104 | thriftUnionB.read(protocol); 105 | 106 | assertThat(thriftUnionB.getSetField()).isEqualTo(com.meituan.firefly.testthrift.UnionB._Fields.OS); 107 | assertThat(thriftUnionB.getOs()).isNotNull(); 108 | assertThat(thriftUnionB.getOs().getId()).isEqualTo(99); 109 | } 110 | 111 | @Test 112 | public void shouldReadComplicatedStruct_thatThriftWrite() throws Exception { 113 | TMemoryBuffer transport = new TMemoryBuffer(4096); 114 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 115 | com.meituan.firefly.testthrift.ComplicatedStruct thriftComplicatedStruct = new com.meituan.firefly.testthrift.ComplicatedStruct(); 116 | thriftComplicatedStruct.setShortSet(new HashSet<>(Arrays.asList((short) 10, (short) 20))); 117 | thriftComplicatedStruct.setIntSet(new HashSet<>(Arrays.asList(30, 40))); 118 | thriftComplicatedStruct.setShortList(Arrays.asList((short) 100, (short) 101)); 119 | thriftComplicatedStruct.setMixStructlist(Arrays.asList(new com.meituan.firefly.testthrift.MixStruct(1, 2))); 120 | Map orderedStructMap = new HashMap<>(); 121 | orderedStructMap.put((short) 1, new com.meituan.firefly.testthrift.OrderedStruct(1)); 122 | thriftComplicatedStruct.setOrderedStructMap(orderedStructMap); 123 | Map mixStructMap = new HashMap<>(); 124 | mixStructMap.put((short) 1, new com.meituan.firefly.testthrift.MixStruct(1, 3)); 125 | thriftComplicatedStruct.setMixStructMap(mixStructMap); 126 | thriftComplicatedStruct.setOrderEnum(com.meituan.firefly.testthrift.OrderEnum.Mix); 127 | thriftComplicatedStruct.setBin(new byte[]{1, 2, 3}); 128 | thriftComplicatedStruct.write(protocol); 129 | 130 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(ComplicatedStruct.class, thrift); 131 | ComplicatedStruct fireflyComplicatedStruct = (ComplicatedStruct) structTypeAdapter.read(protocol); 132 | assertThat(fireflyComplicatedStruct).isNotNull(); 133 | assertThat(fireflyComplicatedStruct.a).isEqualTo(12345); 134 | assertThat(fireflyComplicatedStruct.b).isEqualTo(100l); 135 | assertThat(fireflyComplicatedStruct.c).isEqualTo(99.99); 136 | assertThat(fireflyComplicatedStruct.shortSet).containsOnly((short) 10, (short) 20); 137 | assertThat(fireflyComplicatedStruct.intSet).containsOnly(30, 40); 138 | assertThat(fireflyComplicatedStruct.shortList).containsOnly((short) 100, (short) 101); 139 | assertThat(fireflyComplicatedStruct.mixStructlist).hasSize(1).hasOnlyElementsOfType(MixStruct.class); 140 | assertThat(fireflyComplicatedStruct.orderedStructMap).hasSize(1).containsOnlyKeys((short) 1); 141 | assertThat(fireflyComplicatedStruct.orderedStructMap.get((short) 1)).isExactlyInstanceOf(OrderedStruct.class).is(new Condition() { 142 | @Override 143 | public boolean matches(OrderedStruct value) { 144 | return value.id == 1; 145 | } 146 | }); 147 | assertThat(fireflyComplicatedStruct.mixStructMap).hasSize(1).containsOnlyKeys((short) 1); 148 | assertThat(fireflyComplicatedStruct.mixStructMap.get((short) 1)).isExactlyInstanceOf(MixStruct.class).is(new Condition() { 149 | @Override 150 | public boolean matches(MixStruct value) { 151 | return value.id == 1 && value.uid == 3; 152 | } 153 | }); 154 | assertThat(fireflyComplicatedStruct.orderEnum).isEqualTo(OrderEnum.Mix); 155 | assertThat(fireflyComplicatedStruct.bin).isEqualTo(new byte[]{1, 2, 3}); 156 | } 157 | 158 | @Test 159 | public void shouldWriteComplicatedStruct_thatThriftRead() throws Exception { 160 | TMemoryBuffer transport = new TMemoryBuffer(4096); 161 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 162 | ComplicatedStruct fireflyComplicatedStruct = new ComplicatedStruct(); 163 | assertThat(fireflyComplicatedStruct.a).isEqualTo(12345); 164 | assertThat(fireflyComplicatedStruct.b).isEqualTo(100l); 165 | assertThat(fireflyComplicatedStruct.c).isEqualTo(99.99); 166 | 167 | fireflyComplicatedStruct.shortSet = new HashSet<>(Arrays.asList((short) 10, (short) 20)); 168 | fireflyComplicatedStruct.intSet = new HashSet<>(Arrays.asList(30, 40)); 169 | fireflyComplicatedStruct.shortList = Arrays.asList((short) 100, (short) 101); 170 | MixStruct mixStruct1 = new MixStruct(); 171 | mixStruct1.id = 1; 172 | mixStruct1.uid = 2; 173 | fireflyComplicatedStruct.mixStructlist = Arrays.asList(mixStruct1); 174 | OrderedStruct orderedStruct1 = new OrderedStruct(); 175 | orderedStruct1.id = 1; 176 | Map orderedStructMap = new HashMap<>(); 177 | orderedStructMap.put((short) 1, orderedStruct1); 178 | fireflyComplicatedStruct.orderedStructMap = orderedStructMap; 179 | MixStruct mixStruct2 = new MixStruct(); 180 | mixStruct2.id = 1; 181 | mixStruct2.uid = 3; 182 | Map mixStructMap = new HashMap<>(); 183 | mixStructMap.put((short) 1, mixStruct2); 184 | fireflyComplicatedStruct.mixStructMap = mixStructMap; 185 | fireflyComplicatedStruct.orderEnum = OrderEnum.Mix; 186 | fireflyComplicatedStruct.bin = new byte[]{1, 2, 3}; 187 | StructTypeAdapterFactory.StructTypeAdapter structTypeAdapter = new StructTypeAdapterFactory.StructTypeAdapter(ComplicatedStruct.class, thrift); 188 | structTypeAdapter.write(fireflyComplicatedStruct, protocol); 189 | 190 | com.meituan.firefly.testthrift.ComplicatedStruct thriftComplicatedStruct = new com.meituan.firefly.testthrift.ComplicatedStruct(); 191 | thriftComplicatedStruct.read(protocol); 192 | assertThat(thriftComplicatedStruct.a).isEqualTo(12345); 193 | assertThat(thriftComplicatedStruct.b).isEqualTo(100l); 194 | assertThat(thriftComplicatedStruct.c).isEqualTo(99.99); 195 | assertThat(thriftComplicatedStruct.shortSet).containsOnly((short) 10, (short) 20); 196 | assertThat(thriftComplicatedStruct.intSet).containsOnly(30, 40); 197 | assertThat(thriftComplicatedStruct.shortList).containsOnly((short) 100, (short) 101); 198 | assertThat(thriftComplicatedStruct.mixStructlist).hasSize(1).hasOnlyElementsOfType(com.meituan.firefly.testthrift.MixStruct.class); 199 | assertThat(thriftComplicatedStruct.orderedStructMap).hasSize(1).containsOnlyKeys((short) 1); 200 | assertThat(thriftComplicatedStruct.orderedStructMap.get((short) 1)).isExactlyInstanceOf(com.meituan.firefly.testthrift.OrderedStruct.class).is(new Condition() { 201 | @Override 202 | public boolean matches(com.meituan.firefly.testthrift.OrderedStruct value) { 203 | return value.id == 1; 204 | } 205 | }); 206 | assertThat(thriftComplicatedStruct.mixStructMap).hasSize(1).containsOnlyKeys((short) 1); 207 | assertThat(thriftComplicatedStruct.mixStructMap.get((short) 1)).isExactlyInstanceOf(com.meituan.firefly.testthrift.MixStruct.class).is(new Condition() { 208 | @Override 209 | public boolean matches(com.meituan.firefly.testthrift.MixStruct value) { 210 | return value.id == 1 && value.uid == 3; 211 | } 212 | }); 213 | assertThat(thriftComplicatedStruct.orderEnum).isEqualTo(com.meituan.firefly.testthrift.OrderEnum.Mix); 214 | assertThat(thriftComplicatedStruct.bin.array()).isEqualTo(new byte[]{1, 2, 3}); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testthrift/OrderedStruct.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.2) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | package com.meituan.firefly.testthrift; 8 | 9 | import org.apache.thrift.scheme.IScheme; 10 | import org.apache.thrift.scheme.SchemeFactory; 11 | import org.apache.thrift.scheme.StandardScheme; 12 | 13 | import org.apache.thrift.scheme.TupleScheme; 14 | import org.apache.thrift.protocol.TTupleProtocol; 15 | import org.apache.thrift.protocol.TProtocolException; 16 | import org.apache.thrift.EncodingUtils; 17 | import org.apache.thrift.TException; 18 | import org.apache.thrift.async.AsyncMethodCallback; 19 | import org.apache.thrift.server.AbstractNonblockingServer.*; 20 | import java.util.List; 21 | import java.util.ArrayList; 22 | import java.util.Map; 23 | import java.util.HashMap; 24 | import java.util.EnumMap; 25 | import java.util.Set; 26 | import java.util.HashSet; 27 | import java.util.EnumSet; 28 | import java.util.Collections; 29 | import java.util.BitSet; 30 | import java.nio.ByteBuffer; 31 | import java.util.Arrays; 32 | import javax.annotation.Generated; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) 37 | @Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2015-12-25") 38 | public class OrderedStruct implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { 39 | private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("OrderedStruct"); 40 | 41 | private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1); 42 | 43 | private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); 44 | static { 45 | schemes.put(StandardScheme.class, new OrderedStructStandardSchemeFactory()); 46 | schemes.put(TupleScheme.class, new OrderedStructTupleSchemeFactory()); 47 | } 48 | 49 | public int id; // required 50 | 51 | /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ 52 | public enum _Fields implements org.apache.thrift.TFieldIdEnum { 53 | ID((short)1, "id"); 54 | 55 | private static final Map byName = new HashMap(); 56 | 57 | static { 58 | for (_Fields field : EnumSet.allOf(_Fields.class)) { 59 | byName.put(field.getFieldName(), field); 60 | } 61 | } 62 | 63 | /** 64 | * Find the _Fields constant that matches fieldId, or null if its not found. 65 | */ 66 | public static _Fields findByThriftId(int fieldId) { 67 | switch(fieldId) { 68 | case 1: // ID 69 | return ID; 70 | default: 71 | return null; 72 | } 73 | } 74 | 75 | /** 76 | * Find the _Fields constant that matches fieldId, throwing an exception 77 | * if it is not found. 78 | */ 79 | public static _Fields findByThriftIdOrThrow(int fieldId) { 80 | _Fields fields = findByThriftId(fieldId); 81 | if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); 82 | return fields; 83 | } 84 | 85 | /** 86 | * Find the _Fields constant that matches name, or null if its not found. 87 | */ 88 | public static _Fields findByName(String name) { 89 | return byName.get(name); 90 | } 91 | 92 | private final short _thriftId; 93 | private final String _fieldName; 94 | 95 | _Fields(short thriftId, String fieldName) { 96 | _thriftId = thriftId; 97 | _fieldName = fieldName; 98 | } 99 | 100 | public short getThriftFieldId() { 101 | return _thriftId; 102 | } 103 | 104 | public String getFieldName() { 105 | return _fieldName; 106 | } 107 | } 108 | 109 | // isset id assignments 110 | private static final int __ID_ISSET_ID = 0; 111 | private byte __isset_bitfield = 0; 112 | public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; 113 | static { 114 | Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); 115 | tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.REQUIRED, 116 | new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); 117 | metaDataMap = Collections.unmodifiableMap(tmpMap); 118 | org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(OrderedStruct.class, metaDataMap); 119 | } 120 | 121 | public OrderedStruct() { 122 | } 123 | 124 | public OrderedStruct( 125 | int id) 126 | { 127 | this(); 128 | this.id = id; 129 | setIdIsSet(true); 130 | } 131 | 132 | /** 133 | * Performs a deep copy on other. 134 | */ 135 | public OrderedStruct(OrderedStruct other) { 136 | __isset_bitfield = other.__isset_bitfield; 137 | this.id = other.id; 138 | } 139 | 140 | public OrderedStruct deepCopy() { 141 | return new OrderedStruct(this); 142 | } 143 | 144 | @Override 145 | public void clear() { 146 | setIdIsSet(false); 147 | this.id = 0; 148 | } 149 | 150 | public int getId() { 151 | return this.id; 152 | } 153 | 154 | public OrderedStruct setId(int id) { 155 | this.id = id; 156 | setIdIsSet(true); 157 | return this; 158 | } 159 | 160 | public void unsetId() { 161 | __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID); 162 | } 163 | 164 | /** Returns true if field id is set (has been assigned a value) and false otherwise */ 165 | public boolean isSetId() { 166 | return EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID); 167 | } 168 | 169 | public void setIdIsSet(boolean value) { 170 | __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value); 171 | } 172 | 173 | public void setFieldValue(_Fields field, Object value) { 174 | switch (field) { 175 | case ID: 176 | if (value == null) { 177 | unsetId(); 178 | } else { 179 | setId((Integer)value); 180 | } 181 | break; 182 | 183 | } 184 | } 185 | 186 | public Object getFieldValue(_Fields field) { 187 | switch (field) { 188 | case ID: 189 | return Integer.valueOf(getId()); 190 | 191 | } 192 | throw new IllegalStateException(); 193 | } 194 | 195 | /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ 196 | public boolean isSet(_Fields field) { 197 | if (field == null) { 198 | throw new IllegalArgumentException(); 199 | } 200 | 201 | switch (field) { 202 | case ID: 203 | return isSetId(); 204 | } 205 | throw new IllegalStateException(); 206 | } 207 | 208 | @Override 209 | public boolean equals(Object that) { 210 | if (that == null) 211 | return false; 212 | if (that instanceof OrderedStruct) 213 | return this.equals((OrderedStruct)that); 214 | return false; 215 | } 216 | 217 | public boolean equals(OrderedStruct that) { 218 | if (that == null) 219 | return false; 220 | 221 | boolean this_present_id = true; 222 | boolean that_present_id = true; 223 | if (this_present_id || that_present_id) { 224 | if (!(this_present_id && that_present_id)) 225 | return false; 226 | if (this.id != that.id) 227 | return false; 228 | } 229 | 230 | return true; 231 | } 232 | 233 | @Override 234 | public int hashCode() { 235 | List list = new ArrayList(); 236 | 237 | boolean present_id = true; 238 | list.add(present_id); 239 | if (present_id) 240 | list.add(id); 241 | 242 | return list.hashCode(); 243 | } 244 | 245 | @Override 246 | public int compareTo(OrderedStruct other) { 247 | if (!getClass().equals(other.getClass())) { 248 | return getClass().getName().compareTo(other.getClass().getName()); 249 | } 250 | 251 | int lastComparison = 0; 252 | 253 | lastComparison = Boolean.valueOf(isSetId()).compareTo(other.isSetId()); 254 | if (lastComparison != 0) { 255 | return lastComparison; 256 | } 257 | if (isSetId()) { 258 | lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, other.id); 259 | if (lastComparison != 0) { 260 | return lastComparison; 261 | } 262 | } 263 | return 0; 264 | } 265 | 266 | public _Fields fieldForId(int fieldId) { 267 | return _Fields.findByThriftId(fieldId); 268 | } 269 | 270 | public void read(org.apache.thrift.protocol.TProtocol iprot) throws TException { 271 | schemes.get(iprot.getScheme()).getScheme().read(iprot, this); 272 | } 273 | 274 | public void write(org.apache.thrift.protocol.TProtocol oprot) throws TException { 275 | schemes.get(oprot.getScheme()).getScheme().write(oprot, this); 276 | } 277 | 278 | @Override 279 | public String toString() { 280 | StringBuilder sb = new StringBuilder("OrderedStruct("); 281 | boolean first = true; 282 | 283 | sb.append("id:"); 284 | sb.append(this.id); 285 | first = false; 286 | sb.append(")"); 287 | return sb.toString(); 288 | } 289 | 290 | public void validate() throws TException { 291 | // check for required fields 292 | // alas, we cannot check 'id' because it's a primitive and you chose the non-beans generator. 293 | // check for sub-struct validity 294 | } 295 | 296 | private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { 297 | try { 298 | write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); 299 | } catch (TException te) { 300 | throw new java.io.IOException(te); 301 | } 302 | } 303 | 304 | private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { 305 | try { 306 | // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. 307 | __isset_bitfield = 0; 308 | read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); 309 | } catch (TException te) { 310 | throw new java.io.IOException(te); 311 | } 312 | } 313 | 314 | private static class OrderedStructStandardSchemeFactory implements SchemeFactory { 315 | public OrderedStructStandardScheme getScheme() { 316 | return new OrderedStructStandardScheme(); 317 | } 318 | } 319 | 320 | private static class OrderedStructStandardScheme extends StandardScheme { 321 | 322 | public void read(org.apache.thrift.protocol.TProtocol iprot, OrderedStruct struct) throws TException { 323 | org.apache.thrift.protocol.TField schemeField; 324 | iprot.readStructBegin(); 325 | while (true) 326 | { 327 | schemeField = iprot.readFieldBegin(); 328 | if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 329 | break; 330 | } 331 | switch (schemeField.id) { 332 | case 1: // ID 333 | if (schemeField.type == org.apache.thrift.protocol.TType.I32) { 334 | struct.id = iprot.readI32(); 335 | struct.setIdIsSet(true); 336 | } else { 337 | org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 338 | } 339 | break; 340 | default: 341 | org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 342 | } 343 | iprot.readFieldEnd(); 344 | } 345 | iprot.readStructEnd(); 346 | 347 | // check for required fields of primitive type, which can't be checked in the validate method 348 | if (!struct.isSetId()) { 349 | throw new TProtocolException("Required field 'id' was not found in serialized data! Struct: " + toString()); 350 | } 351 | struct.validate(); 352 | } 353 | 354 | public void write(org.apache.thrift.protocol.TProtocol oprot, OrderedStruct struct) throws TException { 355 | struct.validate(); 356 | 357 | oprot.writeStructBegin(STRUCT_DESC); 358 | oprot.writeFieldBegin(ID_FIELD_DESC); 359 | oprot.writeI32(struct.id); 360 | oprot.writeFieldEnd(); 361 | oprot.writeFieldStop(); 362 | oprot.writeStructEnd(); 363 | } 364 | 365 | } 366 | 367 | private static class OrderedStructTupleSchemeFactory implements SchemeFactory { 368 | public OrderedStructTupleScheme getScheme() { 369 | return new OrderedStructTupleScheme(); 370 | } 371 | } 372 | 373 | private static class OrderedStructTupleScheme extends TupleScheme { 374 | 375 | @Override 376 | public void write(org.apache.thrift.protocol.TProtocol prot, OrderedStruct struct) throws TException { 377 | TTupleProtocol oprot = (TTupleProtocol) prot; 378 | oprot.writeI32(struct.id); 379 | } 380 | 381 | @Override 382 | public void read(org.apache.thrift.protocol.TProtocol prot, OrderedStruct struct) throws TException { 383 | TTupleProtocol iprot = (TTupleProtocol) prot; 384 | struct.id = iprot.readI32(); 385 | struct.setIdIsSet(true); 386 | } 387 | } 388 | 389 | } 390 | 391 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testthrift/UnorderedStruct.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.2) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | package com.meituan.firefly.testthrift; 8 | 9 | import org.apache.thrift.scheme.IScheme; 10 | import org.apache.thrift.scheme.SchemeFactory; 11 | import org.apache.thrift.scheme.StandardScheme; 12 | 13 | import org.apache.thrift.scheme.TupleScheme; 14 | import org.apache.thrift.protocol.TTupleProtocol; 15 | import org.apache.thrift.protocol.TProtocolException; 16 | import org.apache.thrift.EncodingUtils; 17 | import org.apache.thrift.TException; 18 | import org.apache.thrift.async.AsyncMethodCallback; 19 | import org.apache.thrift.server.AbstractNonblockingServer.*; 20 | import java.util.List; 21 | import java.util.ArrayList; 22 | import java.util.Map; 23 | import java.util.HashMap; 24 | import java.util.EnumMap; 25 | import java.util.Set; 26 | import java.util.HashSet; 27 | import java.util.EnumSet; 28 | import java.util.Collections; 29 | import java.util.BitSet; 30 | import java.nio.ByteBuffer; 31 | import java.util.Arrays; 32 | import javax.annotation.Generated; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) 37 | @Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2015-12-25") 38 | public class UnorderedStruct implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { 39 | private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("UnorderedStruct"); 40 | 41 | private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1); 42 | 43 | private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); 44 | static { 45 | schemes.put(StandardScheme.class, new UnorderedStructStandardSchemeFactory()); 46 | schemes.put(TupleScheme.class, new UnorderedStructTupleSchemeFactory()); 47 | } 48 | 49 | public int id; // required 50 | 51 | /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ 52 | public enum _Fields implements org.apache.thrift.TFieldIdEnum { 53 | ID((short)1, "id"); 54 | 55 | private static final Map byName = new HashMap(); 56 | 57 | static { 58 | for (_Fields field : EnumSet.allOf(_Fields.class)) { 59 | byName.put(field.getFieldName(), field); 60 | } 61 | } 62 | 63 | /** 64 | * Find the _Fields constant that matches fieldId, or null if its not found. 65 | */ 66 | public static _Fields findByThriftId(int fieldId) { 67 | switch(fieldId) { 68 | case 1: // ID 69 | return ID; 70 | default: 71 | return null; 72 | } 73 | } 74 | 75 | /** 76 | * Find the _Fields constant that matches fieldId, throwing an exception 77 | * if it is not found. 78 | */ 79 | public static _Fields findByThriftIdOrThrow(int fieldId) { 80 | _Fields fields = findByThriftId(fieldId); 81 | if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); 82 | return fields; 83 | } 84 | 85 | /** 86 | * Find the _Fields constant that matches name, or null if its not found. 87 | */ 88 | public static _Fields findByName(String name) { 89 | return byName.get(name); 90 | } 91 | 92 | private final short _thriftId; 93 | private final String _fieldName; 94 | 95 | _Fields(short thriftId, String fieldName) { 96 | _thriftId = thriftId; 97 | _fieldName = fieldName; 98 | } 99 | 100 | public short getThriftFieldId() { 101 | return _thriftId; 102 | } 103 | 104 | public String getFieldName() { 105 | return _fieldName; 106 | } 107 | } 108 | 109 | // isset id assignments 110 | private static final int __ID_ISSET_ID = 0; 111 | private byte __isset_bitfield = 0; 112 | public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; 113 | static { 114 | Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); 115 | tmpMap.put(_Fields.ID, new org.apache.thrift.meta_data.FieldMetaData("id", org.apache.thrift.TFieldRequirementType.REQUIRED, 116 | new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32))); 117 | metaDataMap = Collections.unmodifiableMap(tmpMap); 118 | org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(UnorderedStruct.class, metaDataMap); 119 | } 120 | 121 | public UnorderedStruct() { 122 | } 123 | 124 | public UnorderedStruct( 125 | int id) 126 | { 127 | this(); 128 | this.id = id; 129 | setIdIsSet(true); 130 | } 131 | 132 | /** 133 | * Performs a deep copy on other. 134 | */ 135 | public UnorderedStruct(UnorderedStruct other) { 136 | __isset_bitfield = other.__isset_bitfield; 137 | this.id = other.id; 138 | } 139 | 140 | public UnorderedStruct deepCopy() { 141 | return new UnorderedStruct(this); 142 | } 143 | 144 | @Override 145 | public void clear() { 146 | setIdIsSet(false); 147 | this.id = 0; 148 | } 149 | 150 | public int getId() { 151 | return this.id; 152 | } 153 | 154 | public UnorderedStruct setId(int id) { 155 | this.id = id; 156 | setIdIsSet(true); 157 | return this; 158 | } 159 | 160 | public void unsetId() { 161 | __isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __ID_ISSET_ID); 162 | } 163 | 164 | /** Returns true if field id is set (has been assigned a value) and false otherwise */ 165 | public boolean isSetId() { 166 | return EncodingUtils.testBit(__isset_bitfield, __ID_ISSET_ID); 167 | } 168 | 169 | public void setIdIsSet(boolean value) { 170 | __isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __ID_ISSET_ID, value); 171 | } 172 | 173 | public void setFieldValue(_Fields field, Object value) { 174 | switch (field) { 175 | case ID: 176 | if (value == null) { 177 | unsetId(); 178 | } else { 179 | setId((Integer)value); 180 | } 181 | break; 182 | 183 | } 184 | } 185 | 186 | public Object getFieldValue(_Fields field) { 187 | switch (field) { 188 | case ID: 189 | return Integer.valueOf(getId()); 190 | 191 | } 192 | throw new IllegalStateException(); 193 | } 194 | 195 | /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ 196 | public boolean isSet(_Fields field) { 197 | if (field == null) { 198 | throw new IllegalArgumentException(); 199 | } 200 | 201 | switch (field) { 202 | case ID: 203 | return isSetId(); 204 | } 205 | throw new IllegalStateException(); 206 | } 207 | 208 | @Override 209 | public boolean equals(Object that) { 210 | if (that == null) 211 | return false; 212 | if (that instanceof UnorderedStruct) 213 | return this.equals((UnorderedStruct)that); 214 | return false; 215 | } 216 | 217 | public boolean equals(UnorderedStruct that) { 218 | if (that == null) 219 | return false; 220 | 221 | boolean this_present_id = true; 222 | boolean that_present_id = true; 223 | if (this_present_id || that_present_id) { 224 | if (!(this_present_id && that_present_id)) 225 | return false; 226 | if (this.id != that.id) 227 | return false; 228 | } 229 | 230 | return true; 231 | } 232 | 233 | @Override 234 | public int hashCode() { 235 | List list = new ArrayList(); 236 | 237 | boolean present_id = true; 238 | list.add(present_id); 239 | if (present_id) 240 | list.add(id); 241 | 242 | return list.hashCode(); 243 | } 244 | 245 | @Override 246 | public int compareTo(UnorderedStruct other) { 247 | if (!getClass().equals(other.getClass())) { 248 | return getClass().getName().compareTo(other.getClass().getName()); 249 | } 250 | 251 | int lastComparison = 0; 252 | 253 | lastComparison = Boolean.valueOf(isSetId()).compareTo(other.isSetId()); 254 | if (lastComparison != 0) { 255 | return lastComparison; 256 | } 257 | if (isSetId()) { 258 | lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.id, other.id); 259 | if (lastComparison != 0) { 260 | return lastComparison; 261 | } 262 | } 263 | return 0; 264 | } 265 | 266 | public _Fields fieldForId(int fieldId) { 267 | return _Fields.findByThriftId(fieldId); 268 | } 269 | 270 | public void read(org.apache.thrift.protocol.TProtocol iprot) throws TException { 271 | schemes.get(iprot.getScheme()).getScheme().read(iprot, this); 272 | } 273 | 274 | public void write(org.apache.thrift.protocol.TProtocol oprot) throws TException { 275 | schemes.get(oprot.getScheme()).getScheme().write(oprot, this); 276 | } 277 | 278 | @Override 279 | public String toString() { 280 | StringBuilder sb = new StringBuilder("UnorderedStruct("); 281 | boolean first = true; 282 | 283 | sb.append("id:"); 284 | sb.append(this.id); 285 | first = false; 286 | sb.append(")"); 287 | return sb.toString(); 288 | } 289 | 290 | public void validate() throws TException { 291 | // check for required fields 292 | // alas, we cannot check 'id' because it's a primitive and you chose the non-beans generator. 293 | // check for sub-struct validity 294 | } 295 | 296 | private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { 297 | try { 298 | write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); 299 | } catch (TException te) { 300 | throw new java.io.IOException(te); 301 | } 302 | } 303 | 304 | private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { 305 | try { 306 | // it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor. 307 | __isset_bitfield = 0; 308 | read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); 309 | } catch (TException te) { 310 | throw new java.io.IOException(te); 311 | } 312 | } 313 | 314 | private static class UnorderedStructStandardSchemeFactory implements SchemeFactory { 315 | public UnorderedStructStandardScheme getScheme() { 316 | return new UnorderedStructStandardScheme(); 317 | } 318 | } 319 | 320 | private static class UnorderedStructStandardScheme extends StandardScheme { 321 | 322 | public void read(org.apache.thrift.protocol.TProtocol iprot, UnorderedStruct struct) throws TException { 323 | org.apache.thrift.protocol.TField schemeField; 324 | iprot.readStructBegin(); 325 | while (true) 326 | { 327 | schemeField = iprot.readFieldBegin(); 328 | if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 329 | break; 330 | } 331 | switch (schemeField.id) { 332 | case 1: // ID 333 | if (schemeField.type == org.apache.thrift.protocol.TType.I32) { 334 | struct.id = iprot.readI32(); 335 | struct.setIdIsSet(true); 336 | } else { 337 | org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 338 | } 339 | break; 340 | default: 341 | org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 342 | } 343 | iprot.readFieldEnd(); 344 | } 345 | iprot.readStructEnd(); 346 | 347 | // check for required fields of primitive type, which can't be checked in the validate method 348 | if (!struct.isSetId()) { 349 | throw new TProtocolException("Required field 'id' was not found in serialized data! Struct: " + toString()); 350 | } 351 | struct.validate(); 352 | } 353 | 354 | public void write(org.apache.thrift.protocol.TProtocol oprot, UnorderedStruct struct) throws TException { 355 | struct.validate(); 356 | 357 | oprot.writeStructBegin(STRUCT_DESC); 358 | oprot.writeFieldBegin(ID_FIELD_DESC); 359 | oprot.writeI32(struct.id); 360 | oprot.writeFieldEnd(); 361 | oprot.writeFieldStop(); 362 | oprot.writeStructEnd(); 363 | } 364 | 365 | } 366 | 367 | private static class UnorderedStructTupleSchemeFactory implements SchemeFactory { 368 | public UnorderedStructTupleScheme getScheme() { 369 | return new UnorderedStructTupleScheme(); 370 | } 371 | } 372 | 373 | private static class UnorderedStructTupleScheme extends TupleScheme { 374 | 375 | @Override 376 | public void write(org.apache.thrift.protocol.TProtocol prot, UnorderedStruct struct) throws TException { 377 | TTupleProtocol oprot = (TTupleProtocol) prot; 378 | oprot.writeI32(struct.id); 379 | } 380 | 381 | @Override 382 | public void read(org.apache.thrift.protocol.TProtocol prot, UnorderedStruct struct) throws TException { 383 | TTupleProtocol iprot = (TTupleProtocol) prot; 384 | struct.id = iprot.readI32(); 385 | struct.setIdIsSet(true); 386 | } 387 | } 388 | 389 | } 390 | 391 | -------------------------------------------------------------------------------- /library/src/test/java/com/meituan/firefly/testthrift/TestException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Autogenerated by Thrift Compiler (0.9.2) 3 | * 4 | * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING 5 | * @generated 6 | */ 7 | package com.meituan.firefly.testthrift; 8 | 9 | import org.apache.thrift.scheme.IScheme; 10 | import org.apache.thrift.scheme.SchemeFactory; 11 | import org.apache.thrift.scheme.StandardScheme; 12 | 13 | import org.apache.thrift.scheme.TupleScheme; 14 | import org.apache.thrift.protocol.TTupleProtocol; 15 | import org.apache.thrift.protocol.TProtocolException; 16 | import org.apache.thrift.EncodingUtils; 17 | import org.apache.thrift.TException; 18 | import org.apache.thrift.async.AsyncMethodCallback; 19 | import org.apache.thrift.server.AbstractNonblockingServer.*; 20 | import java.util.List; 21 | import java.util.ArrayList; 22 | import java.util.Map; 23 | import java.util.HashMap; 24 | import java.util.EnumMap; 25 | import java.util.Set; 26 | import java.util.HashSet; 27 | import java.util.EnumSet; 28 | import java.util.Collections; 29 | import java.util.BitSet; 30 | import java.nio.ByteBuffer; 31 | import java.util.Arrays; 32 | import javax.annotation.Generated; 33 | import org.slf4j.Logger; 34 | import org.slf4j.LoggerFactory; 35 | 36 | @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) 37 | @Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2015-12-25") 38 | public class TestException extends TException implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { 39 | private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("TestException"); 40 | 41 | private static final org.apache.thrift.protocol.TField MESSAGE_FIELD_DESC = new org.apache.thrift.protocol.TField("message", org.apache.thrift.protocol.TType.STRING, (short)1); 42 | 43 | private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); 44 | static { 45 | schemes.put(StandardScheme.class, new TestExceptionStandardSchemeFactory()); 46 | schemes.put(TupleScheme.class, new TestExceptionTupleSchemeFactory()); 47 | } 48 | 49 | public String message; // required 50 | 51 | /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ 52 | public enum _Fields implements org.apache.thrift.TFieldIdEnum { 53 | MESSAGE((short)1, "message"); 54 | 55 | private static final Map byName = new HashMap(); 56 | 57 | static { 58 | for (_Fields field : EnumSet.allOf(_Fields.class)) { 59 | byName.put(field.getFieldName(), field); 60 | } 61 | } 62 | 63 | /** 64 | * Find the _Fields constant that matches fieldId, or null if its not found. 65 | */ 66 | public static _Fields findByThriftId(int fieldId) { 67 | switch(fieldId) { 68 | case 1: // MESSAGE 69 | return MESSAGE; 70 | default: 71 | return null; 72 | } 73 | } 74 | 75 | /** 76 | * Find the _Fields constant that matches fieldId, throwing an exception 77 | * if it is not found. 78 | */ 79 | public static _Fields findByThriftIdOrThrow(int fieldId) { 80 | _Fields fields = findByThriftId(fieldId); 81 | if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); 82 | return fields; 83 | } 84 | 85 | /** 86 | * Find the _Fields constant that matches name, or null if its not found. 87 | */ 88 | public static _Fields findByName(String name) { 89 | return byName.get(name); 90 | } 91 | 92 | private final short _thriftId; 93 | private final String _fieldName; 94 | 95 | _Fields(short thriftId, String fieldName) { 96 | _thriftId = thriftId; 97 | _fieldName = fieldName; 98 | } 99 | 100 | public short getThriftFieldId() { 101 | return _thriftId; 102 | } 103 | 104 | public String getFieldName() { 105 | return _fieldName; 106 | } 107 | } 108 | 109 | // isset id assignments 110 | public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; 111 | static { 112 | Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); 113 | tmpMap.put(_Fields.MESSAGE, new org.apache.thrift.meta_data.FieldMetaData("message", org.apache.thrift.TFieldRequirementType.DEFAULT, 114 | new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); 115 | metaDataMap = Collections.unmodifiableMap(tmpMap); 116 | org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(TestException.class, metaDataMap); 117 | } 118 | 119 | public TestException() { 120 | } 121 | 122 | public TestException( 123 | String message) 124 | { 125 | this(); 126 | this.message = message; 127 | } 128 | 129 | /** 130 | * Performs a deep copy on other. 131 | */ 132 | public TestException(TestException other) { 133 | if (other.isSetMessage()) { 134 | this.message = other.message; 135 | } 136 | } 137 | 138 | public TestException deepCopy() { 139 | return new TestException(this); 140 | } 141 | 142 | @Override 143 | public void clear() { 144 | this.message = null; 145 | } 146 | 147 | public String getMessage() { 148 | return this.message; 149 | } 150 | 151 | public TestException setMessage(String message) { 152 | this.message = message; 153 | return this; 154 | } 155 | 156 | public void unsetMessage() { 157 | this.message = null; 158 | } 159 | 160 | /** Returns true if field message is set (has been assigned a value) and false otherwise */ 161 | public boolean isSetMessage() { 162 | return this.message != null; 163 | } 164 | 165 | public void setMessageIsSet(boolean value) { 166 | if (!value) { 167 | this.message = null; 168 | } 169 | } 170 | 171 | public void setFieldValue(_Fields field, Object value) { 172 | switch (field) { 173 | case MESSAGE: 174 | if (value == null) { 175 | unsetMessage(); 176 | } else { 177 | setMessage((String)value); 178 | } 179 | break; 180 | 181 | } 182 | } 183 | 184 | public Object getFieldValue(_Fields field) { 185 | switch (field) { 186 | case MESSAGE: 187 | return getMessage(); 188 | 189 | } 190 | throw new IllegalStateException(); 191 | } 192 | 193 | /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ 194 | public boolean isSet(_Fields field) { 195 | if (field == null) { 196 | throw new IllegalArgumentException(); 197 | } 198 | 199 | switch (field) { 200 | case MESSAGE: 201 | return isSetMessage(); 202 | } 203 | throw new IllegalStateException(); 204 | } 205 | 206 | @Override 207 | public boolean equals(Object that) { 208 | if (that == null) 209 | return false; 210 | if (that instanceof TestException) 211 | return this.equals((TestException)that); 212 | return false; 213 | } 214 | 215 | public boolean equals(TestException that) { 216 | if (that == null) 217 | return false; 218 | 219 | boolean this_present_message = true && this.isSetMessage(); 220 | boolean that_present_message = true && that.isSetMessage(); 221 | if (this_present_message || that_present_message) { 222 | if (!(this_present_message && that_present_message)) 223 | return false; 224 | if (!this.message.equals(that.message)) 225 | return false; 226 | } 227 | 228 | return true; 229 | } 230 | 231 | @Override 232 | public int hashCode() { 233 | List list = new ArrayList(); 234 | 235 | boolean present_message = true && (isSetMessage()); 236 | list.add(present_message); 237 | if (present_message) 238 | list.add(message); 239 | 240 | return list.hashCode(); 241 | } 242 | 243 | @Override 244 | public int compareTo(TestException other) { 245 | if (!getClass().equals(other.getClass())) { 246 | return getClass().getName().compareTo(other.getClass().getName()); 247 | } 248 | 249 | int lastComparison = 0; 250 | 251 | lastComparison = Boolean.valueOf(isSetMessage()).compareTo(other.isSetMessage()); 252 | if (lastComparison != 0) { 253 | return lastComparison; 254 | } 255 | if (isSetMessage()) { 256 | lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.message, other.message); 257 | if (lastComparison != 0) { 258 | return lastComparison; 259 | } 260 | } 261 | return 0; 262 | } 263 | 264 | public _Fields fieldForId(int fieldId) { 265 | return _Fields.findByThriftId(fieldId); 266 | } 267 | 268 | public void read(org.apache.thrift.protocol.TProtocol iprot) throws TException { 269 | schemes.get(iprot.getScheme()).getScheme().read(iprot, this); 270 | } 271 | 272 | public void write(org.apache.thrift.protocol.TProtocol oprot) throws TException { 273 | schemes.get(oprot.getScheme()).getScheme().write(oprot, this); 274 | } 275 | 276 | @Override 277 | public String toString() { 278 | StringBuilder sb = new StringBuilder("TestException("); 279 | boolean first = true; 280 | 281 | sb.append("message:"); 282 | if (this.message == null) { 283 | sb.append("null"); 284 | } else { 285 | sb.append(this.message); 286 | } 287 | first = false; 288 | sb.append(")"); 289 | return sb.toString(); 290 | } 291 | 292 | public void validate() throws TException { 293 | // check for required fields 294 | // check for sub-struct validity 295 | } 296 | 297 | private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { 298 | try { 299 | write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); 300 | } catch (TException te) { 301 | throw new java.io.IOException(te); 302 | } 303 | } 304 | 305 | private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { 306 | try { 307 | read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); 308 | } catch (TException te) { 309 | throw new java.io.IOException(te); 310 | } 311 | } 312 | 313 | private static class TestExceptionStandardSchemeFactory implements SchemeFactory { 314 | public TestExceptionStandardScheme getScheme() { 315 | return new TestExceptionStandardScheme(); 316 | } 317 | } 318 | 319 | private static class TestExceptionStandardScheme extends StandardScheme { 320 | 321 | public void read(org.apache.thrift.protocol.TProtocol iprot, TestException struct) throws TException { 322 | org.apache.thrift.protocol.TField schemeField; 323 | iprot.readStructBegin(); 324 | while (true) 325 | { 326 | schemeField = iprot.readFieldBegin(); 327 | if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 328 | break; 329 | } 330 | switch (schemeField.id) { 331 | case 1: // MESSAGE 332 | if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { 333 | struct.message = iprot.readString(); 334 | struct.setMessageIsSet(true); 335 | } else { 336 | org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 337 | } 338 | break; 339 | default: 340 | org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); 341 | } 342 | iprot.readFieldEnd(); 343 | } 344 | iprot.readStructEnd(); 345 | 346 | // check for required fields of primitive type, which can't be checked in the validate method 347 | struct.validate(); 348 | } 349 | 350 | public void write(org.apache.thrift.protocol.TProtocol oprot, TestException struct) throws TException { 351 | struct.validate(); 352 | 353 | oprot.writeStructBegin(STRUCT_DESC); 354 | if (struct.message != null) { 355 | oprot.writeFieldBegin(MESSAGE_FIELD_DESC); 356 | oprot.writeString(struct.message); 357 | oprot.writeFieldEnd(); 358 | } 359 | oprot.writeFieldStop(); 360 | oprot.writeStructEnd(); 361 | } 362 | 363 | } 364 | 365 | private static class TestExceptionTupleSchemeFactory implements SchemeFactory { 366 | public TestExceptionTupleScheme getScheme() { 367 | return new TestExceptionTupleScheme(); 368 | } 369 | } 370 | 371 | private static class TestExceptionTupleScheme extends TupleScheme { 372 | 373 | @Override 374 | public void write(org.apache.thrift.protocol.TProtocol prot, TestException struct) throws TException { 375 | TTupleProtocol oprot = (TTupleProtocol) prot; 376 | BitSet optionals = new BitSet(); 377 | if (struct.isSetMessage()) { 378 | optionals.set(0); 379 | } 380 | oprot.writeBitSet(optionals, 1); 381 | if (struct.isSetMessage()) { 382 | oprot.writeString(struct.message); 383 | } 384 | } 385 | 386 | @Override 387 | public void read(org.apache.thrift.protocol.TProtocol prot, TestException struct) throws TException { 388 | TTupleProtocol iprot = (TTupleProtocol) prot; 389 | BitSet incoming = iprot.readBitSet(1); 390 | if (incoming.get(0)) { 391 | struct.message = iprot.readString(); 392 | struct.setMessageIsSet(true); 393 | } 394 | } 395 | } 396 | 397 | } 398 | 399 | --------------------------------------------------------------------------------