├── 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 super Object> 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 |
--------------------------------------------------------------------------------