Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Dockerfile for building artifacts
FROM gradle:5.4.1-jdk8
MAINTAINER chris@thinkdataworks.com

ENV APPDIR /app
ENV PROTOC_VERSION 3.5.1

WORKDIR $APPDIR
RUN mkdir -p $APPDIR

RUN apt-get update -yqq && \
apt-get install -yqq build-essential

ENV CLASSPATH ".:/usr/local/lib:$CLASSPATH"

# install protoc; used for protobuff stuff
RUN curl -OL https://storage.googleapis.com/tdw-static/mirrored/protoc-${PROTOC_VERSION}-linux-x86_64.zip && \
unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d protoc3 && \
mv protoc3/bin/* /usr/local/bin/ && \
mv protoc3/include/* /usr/local/include/

COPY . $APPDIR
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ To build the various components, run the following:
git clone git@github.com:devork/flit.git
cd flit
./gradlew clean build pack

### Building with docker

```bash
$ docker-compose run gen ./gradlew build pack
```

### Installation

Expand Down Expand Up @@ -66,7 +72,7 @@ The flit plugin accepts the following plugin parameters:

| Name | Required | Type | Description |
|:--------------|:---------:|:------------------------------|:----------------------------------------------------------|
| `target` | Y | `enum[server]` | The type of target to generate e.g. server, client etc |
| `target` | Y | `enum[server,client]` | The type of target to generate e.g. server, client etc |
| `type` | Y | `enum[spring,undertow,boot]` | Type of target to generate |
| `context` | N | `string` | Base context for routing, default is `/twirp` |

Expand Down
8 changes: 8 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: "3.4"

services:
gen:
build: .
volumes:
- .:/app
command: sleep 10000
1 change: 1 addition & 0 deletions plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies {
compile 'com.google.protobuf:protobuf-java:3.5.1'
compile 'com.google.protobuf:protobuf-java-util:3.5.1'
compile 'com.squareup:javapoet:1.11.1'
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.0.0'

compileOnly('org.projectlombok:lombok:+')

Expand Down
2 changes: 1 addition & 1 deletion plugin/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=1.0.0
version=1.1.0
8 changes: 8 additions & 0 deletions plugin/src/main/java/com/flit/protoc/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.flit.protoc.gen.Generator;
import com.flit.protoc.gen.GeneratorException;
import com.flit.protoc.gen.client.okhttp.OkHttpGenerator;
import com.flit.protoc.gen.server.spring.SpringGenerator;
import com.flit.protoc.gen.server.undertow.UndertowGenerator;
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest;
Expand Down Expand Up @@ -53,6 +54,13 @@ private Generator resolveGenerator(Map<String, Parameter> params) {
default:
throw new GeneratorException("Unknown server type: " + params.get(PARAM_TYPE).getValue());
}
case "client":
switch(params.get(PARAM_TYPE).getValue()) {
case "okhttp":
return new OkHttpGenerator();
default:
throw new GeneratorException("Unknown client type: " + params.get(PARAM_TYPE).getValue());
}
default:
throw new GeneratorException("Unknown target type: " + params.get(PARAM_TARGET).getValue());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.flit.protoc.gen.server;
package com.flit.protoc.gen;

import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.compiler.PluginProtos;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.flit.protoc.gen.server;
package com.flit.protoc.gen;

import com.google.protobuf.DescriptorProtos;
import com.squareup.javapoet.ClassName;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.flit.protoc.gen.client;

import com.flit.protoc.Parameter;
import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.Generator;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.compiler.PluginProtos;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.flit.protoc.Parameter.PARAM_CONTEXT;

public abstract class BaseClientGenerator implements Generator {
@Override public List<PluginProtos.CodeGeneratorResponse.File> generate(PluginProtos.CodeGeneratorRequest request, Map<String, Parameter> params) {
List<PluginProtos.CodeGeneratorResponse.File> files = new ArrayList<>();
String context = getContext(params);
TypeMapper mapper = new TypeMapper(request.getProtoFileList());
request.getProtoFileList().forEach(proto -> {
proto.getServiceList().forEach(s -> {
files.addAll(new ClientGenerator(proto, s, mapper).getFiles());
files.addAll(getRpcGenerator(proto, s, context, mapper).getFiles());
});
});
return files;
}

private static String getContext(Map<String, Parameter> params) {
if (params.containsKey(PARAM_CONTEXT)) {
return params.get(PARAM_CONTEXT).getValue();
}
return null;
}

protected abstract BaseGenerator getRpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.flit.protoc.gen.client;

import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.compiler.PluginProtos;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import javax.lang.model.element.Modifier;
import java.util.Collections;
import java.util.List;

public class ClientGenerator extends BaseGenerator {
private final TypeSpec.Builder rpcInterface;

public ClientGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s, TypeMapper mapper) {
super(proto, s, mapper);
rpcInterface = TypeSpec.interfaceBuilder(ClassName.get(javaPackage, "Rpc" + service.getName()));
rpcInterface.addModifiers(Modifier.PUBLIC);
service.getMethodList().forEach(this::addDispatchMethod);
}

private void addDispatchMethod(DescriptorProtos.MethodDescriptorProto m) {
rpcInterface.addMethod(MethodSpec.methodBuilder("dispatch" + m.getName())
.addParameter(mapper.get(m.getInputType()), "in")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(mapper.get(m.getOutputType()))
.addException(Exception.class)
.build());
}

@Override
public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
return Collections.singletonList(toFile(getServiceInterface(), rpcInterface.build()));
}
}
20 changes: 20 additions & 0 deletions plugin/src/main/java/com/flit/protoc/gen/client/Types.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.flit.protoc.gen.client;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;

import java.util.function.Function;

public class Types {
public static final ClassName String = ClassName.get(String.class);
public static final ClassName OkHttpClient = ClassName.bestGuess("okhttp3.OkHttpClient");
public static final ClassName Exception = ClassName.get(java.lang.Exception.class);
public static final ClassName RequestBody = ClassName.bestGuess("okhttp3.RequestBody");
public static final ClassName RequestBuilder = ClassName.bestGuess("okhttp3.Request.Builder");
public static final ClassName MediaType = ClassName.bestGuess("okhttp3.MediaType");
public static final ClassName InputStream = ClassName.bestGuess("java.io.InputStream");
public static final ClassName HttpUrl = ClassName.bestGuess("okhttp3.HttpUrl");
public static final ClassName Request = ClassName.bestGuess("okhttp3.Request");
public static final ClassName Response = ClassName.bestGuess("okhttp3.Response");
public static final ParameterizedTypeName BeforeRequestFunction = ParameterizedTypeName.get(ClassName.bestGuess("java.util.function.Function"), ClassName.bestGuess("BeforeRequestPayload"), RequestBuilder);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.flit.protoc.gen.client.okhttp;

import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.client.BaseClientGenerator;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos;

public class OkHttpGenerator extends BaseClientGenerator {
@Override
protected BaseGenerator getRpcGenerator(
DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) {
return new RpcGenerator(proto, service, context, mapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.flit.protoc.gen.client.okhttp;

import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.compiler.PluginProtos;
import com.squareup.javapoet.*;

import java.util.Collections;
import java.util.List;

import static com.flit.protoc.gen.server.Types.ErrorCode;
import static com.flit.protoc.gen.server.Types.FlitException;
import static javax.lang.model.element.Modifier.*;
import static com.flit.protoc.gen.client.Types.*;

public class RpcGenerator extends BaseGenerator {
private final String context;
private final TypeSpec.Builder rpcDispatcher;

RpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context, TypeMapper mapper) {
super(proto, service, mapper);
this.context = getContext(context);
rpcDispatcher = TypeSpec.classBuilder(getClientName(service))
.addModifiers(PUBLIC);
addRequestPayload();
addStaticFields();
addInstanceFields();
addConstructor();
addBeforeRequestFunctionSetter();
service.getMethodList().forEach(this::writeDispatchMethod);
}

private ClassName getClientName(DescriptorProtos.ServiceDescriptorProto service) {
return ClassName.get(javaPackage, "Rpc" + service.getName() + "Dispatcher");
}

private void addBeforeRequestFunctionSetter() {
rpcDispatcher.addMethod(
MethodSpec.methodBuilder("setBeforeRequest")
.addModifiers(PUBLIC)
.addParameter(BeforeRequestFunction, "beforeRequest")
.addStatement("this.beforeRequest = beforeRequest")
.build()
);
}

private void addRequestPayload() {
// Add inner class for request payload
TypeSpec.Builder payloadBuilder = TypeSpec.classBuilder("BeforeRequestPayload").addModifiers(PUBLIC);
payloadBuilder.addMethod(MethodSpec.constructorBuilder()
.addModifiers(PUBLIC)
.addParameter(RequestBuilder, "builder")
.addParameter(HttpUrl, "url")
.addStatement("this.builder = builder")
.addStatement("this.url = url")
.build());
payloadBuilder.addField(FieldSpec.builder(RequestBuilder, "builder").addModifiers(PUBLIC).build());
payloadBuilder.addField(FieldSpec.builder(HttpUrl, "url").addModifiers(PUBLIC).build());

// Add to dispatcher
rpcDispatcher.addType(payloadBuilder.build());
}

private void addStaticFields() {
// Add service path prix
rpcDispatcher.addField(FieldSpec.builder(String, "SERVICE_PATH_PREFIX")
.addModifiers(PUBLIC, STATIC, FINAL)
.initializer("\"$L/$L$L/\"", context, proto.hasPackage() ? proto.getPackage() + "." : "", service.getName())
.build());
}

private void addInstanceFields() {
rpcDispatcher.addField(FieldSpec.builder(String, "baseAddress").addModifiers(PRIVATE, FINAL).build());
rpcDispatcher.addField(FieldSpec.builder(OkHttpClient, "client").addModifiers(PRIVATE).build());
rpcDispatcher.addField(FieldSpec.builder(BeforeRequestFunction, "beforeRequest").build());
}

private void addConstructor() {
rpcDispatcher.addMethod(MethodSpec.constructorBuilder()
.addModifiers(PUBLIC)
.addParameter(String, "address")
.addParameter(OkHttpClient, "client")
.addStatement("this.baseAddress = address")
.addStatement("this.client = client")
.build());
}

private void writeDispatchMethod(DescriptorProtos.MethodDescriptorProto m) {
ClassName inputType = mapper.get(m.getInputType());
ClassName outputType = mapper.get(m.getOutputType());
rpcDispatcher.addMethod(
MethodSpec.methodBuilder("dispatch" + m.getName())
.addModifiers(PUBLIC)
.addParameter(inputType, "in")
.returns(outputType)
.addException(Exception)
.addStatement("$T url = $T.parse(baseAddress + SERVICE_PATH_PREFIX + $S)", HttpUrl, HttpUrl, m.getName())
.addStatement("$T requestBody = $T.create(in.toByteArray(), $T.get(\"application/protobuf\"))", RequestBody, RequestBody, MediaType)
.addStatement("$T builder = new $T()", RequestBuilder, RequestBuilder)
.beginControlFlow("if(beforeRequest != null)")
.addStatement("builder = beforeRequest.apply(new BeforeRequestPayload(builder, url))")
.endControlFlow()
.addStatement("builder.addHeader(\"Accept\", \"application/protobuf\")")
.addStatement("builder.addHeader(\"Content-Type\", \"application/protobuf\")")
.addStatement("builder.addHeader(\"Flit-Version\", \"v1.1.0\")")
.addStatement("builder.url(url)")
.addStatement("builder.post(requestBody)")
.addStatement("$T request = builder.build()", Request)
.addStatement("String responseString")
.addStatement("$T responseStream", InputStream)
.beginControlFlow("try($T response = client.newCall(request).execute())", Response)
.beginControlFlow("if(response.code() != 200)")
.addStatement("responseString = response.body().string()")
.addStatement("throw $T.builder().withErrorCode($T.INTERNAL).withMeta(\"message\", responseString).withMessage(\"RPC error\").build()", FlitException, ErrorCode)
.nextControlFlow("else")
.addStatement("responseStream = response.body().byteStream()")
.addStatement("return $T.parseFrom(responseStream)", outputType)
.endControlFlow()
.endControlFlow()
.build()
);
}

@Override
public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
return Collections.singletonList(toFile(getClientName(service), rpcDispatcher.build()));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.flit.protoc.gen.server;

import com.flit.protoc.Parameter;
import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.Generator;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.flit.protoc.gen.server;

import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.compiler.PluginProtos;
import com.squareup.javapoet.ClassName;
Expand Down Expand Up @@ -34,7 +36,8 @@ private void addHandleMethod(DescriptorProtos.MethodDescriptorProto m) {
.build());
}

@Override public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
@Override
public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
return Collections.singletonList(toFile(getServiceInterface(), rpcInterface.build()));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.flit.protoc.gen.server.spring;

import com.flit.protoc.gen.server.BaseGenerator;
import com.flit.protoc.gen.server.TypeMapper;
import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.TypeMapper;
import com.flit.protoc.gen.server.Types;
import com.google.protobuf.DescriptorProtos;
import com.google.protobuf.compiler.PluginProtos;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.flit.protoc.gen.server.spring;

import com.flit.protoc.gen.server.BaseGenerator;
import com.flit.protoc.gen.BaseGenerator;
import com.flit.protoc.gen.server.BaseServerGenerator;
import com.flit.protoc.gen.server.TypeMapper;
import com.flit.protoc.gen.TypeMapper;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto;

Expand Down
Loading