Skip to content

Commit c151fb0

Browse files
author
Alex Davies-Moore
committed
Added undertow service generator
1 parent 07d8f42 commit c151fb0

15 files changed

Lines changed: 466 additions & 24 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@
2626

2727
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
2828
hs_err_pid*
29+
plugin/out

plugin/src/main/java/com/flit/protoc/Plugin.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.flit.protoc.gen.Generator;
44
import com.flit.protoc.gen.GeneratorException;
55
import com.flit.protoc.gen.server.spring.SpringGenerator;
6+
import com.flit.protoc.gen.server.undertow.UndertowGenerator;
67
import com.google.protobuf.compiler.PluginProtos;
78

89
import java.util.Map;
@@ -22,7 +23,7 @@ public PluginProtos.CodeGeneratorResponse process() {
2223
if (!request.hasParameter()) {
2324
return PluginProtos.CodeGeneratorResponse
2425
.newBuilder()
25-
.setError("Usage: --flit_out=target=server,type=[spring]:<PATH>")
26+
.setError("Usage: --flit_out=target=server,type=[spring|undertow]:<PATH>")
2627
.build();
2728
}
2829

@@ -58,6 +59,8 @@ private Generator resolveGenerator(Map<String, Parameter> params) {
5859
case "boot":
5960
case "spring":
6061
return new SpringGenerator();
62+
case "undertow":
63+
return new UndertowGenerator();
6164
default:
6265
throw new GeneratorException("Unknown server type: " + params.get(PARAM_TYPE).getValue());
6366
}

plugin/src/main/java/com/flit/protoc/gen/server/spring/BaseGenerator.java renamed to plugin/src/main/java/com/flit/protoc/gen/server/BaseGenerator.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.flit.protoc.gen.server.spring;
1+
package com.flit.protoc.gen.server;
22

33
import com.flit.protoc.gen.Buffer;
44
import com.google.protobuf.DescriptorProtos;
@@ -7,16 +7,16 @@
77
import java.time.Instant;
88
import java.util.List;
99

10-
abstract class BaseGenerator {
11-
Buffer b = new Buffer();
10+
public abstract class BaseGenerator {
11+
protected Buffer b = new Buffer();
1212

13-
DescriptorProtos.ServiceDescriptorProto service;
14-
String javaPackage;
15-
String clazz;
13+
protected DescriptorProtos.ServiceDescriptorProto service;
14+
protected String javaPackage;
15+
protected String clazz;
1616

17-
DescriptorProtos.FileDescriptorProto proto;
17+
protected DescriptorProtos.FileDescriptorProto proto;
1818

19-
BaseGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s) {
19+
protected BaseGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s) {
2020
this.clazz = proto.getOptions().getJavaOuterClassname();
2121

2222
if (this.clazz == null || this.clazz.isEmpty()) {
@@ -62,26 +62,26 @@ abstract class BaseGenerator {
6262
this.service = s;
6363
}
6464

65-
void writeProlog() {
65+
public void writeProlog() {
6666
b.wn("// -------------------------------------------------------------");
6767
b.wn("// Generated code from flit: Please do not modify");
6868
b.wn("// Created: ", Instant.now().toString());
6969
b.wn("// -------------------------------------------------------------");
7070
b.wn("\n");
7171
}
7272

73-
void writePackage() {
73+
public void writePackage() {
7474
b.wn("package ", javaPackage, ";");
7575
b.n();
7676
}
7777

78-
abstract List<PluginProtos.CodeGeneratorResponse.File> getFiles();
78+
public abstract List<PluginProtos.CodeGeneratorResponse.File> getFiles();
7979

80-
static String basename(String name) {
80+
public static String basename(String name) {
8181
return basename(name, "\\.");
8282
}
8383

84-
static String basename(String name, String sep) {
84+
public static String basename(String name, String sep) {
8585
String[] parts = name.split(sep);
8686

8787
return parts[parts.length - 1];

plugin/src/main/java/com/flit/protoc/gen/server/spring/ServiceGenerator.java renamed to plugin/src/main/java/com/flit/protoc/gen/server/ServiceGenerator.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
package com.flit.protoc.gen.server.spring;
1+
package com.flit.protoc.gen.server;
22

33
import com.google.protobuf.DescriptorProtos;
44
import com.google.protobuf.compiler.PluginProtos;
55

66
import java.util.Collections;
77
import java.util.List;
88

9-
class ServiceGenerator extends BaseGenerator {
9+
public class ServiceGenerator extends BaseGenerator {
1010

1111
private final String filename;
1212

13-
ServiceGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s) {
13+
public ServiceGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto s) {
1414
super(proto, s);
1515
this.filename = javaPackage.replace(".", "/") + "/Rpc" + service.getName() + "Service.java";
1616
}
1717

18-
void open() {
18+
public void open() {
1919
b.wn("public interface Rpc", service.getName(), "Service {");
2020
b.n();
2121
b.inc();
2222
}
2323

24-
void close() {
24+
public void close() {
2525
b.dec();
2626
b.wn("}");
2727
}
2828

29-
void writeService(DescriptorProtos.ServiceDescriptorProto s) {
29+
public void writeService(DescriptorProtos.ServiceDescriptorProto s) {
3030

3131
s.getMethodList().forEach(m -> {
3232
b.iwn(clazz, ".", basename(m.getOutputType()), " handle", m.getName(), "(", clazz, ".", basename(m.getInputType())," in);");
@@ -36,7 +36,7 @@ void writeService(DescriptorProtos.ServiceDescriptorProto s) {
3636
}
3737

3838
@Override
39-
List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
39+
public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
4040
PluginProtos.CodeGeneratorResponse.File.Builder builder = PluginProtos.CodeGeneratorResponse.File.newBuilder();
4141
builder.setName(filename);
4242
builder.setContent(b.toString());

plugin/src/main/java/com/flit/protoc/gen/server/spring/RpcGenerator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.flit.protoc.gen.server.spring;
22

3+
import com.flit.protoc.gen.server.BaseGenerator;
34
import com.google.protobuf.DescriptorProtos;
45
import com.google.protobuf.compiler.PluginProtos;
56

@@ -126,7 +127,7 @@ void writeService(DescriptorProtos.ServiceDescriptorProto s) {
126127
}
127128

128129
@Override
129-
List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
130+
public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
130131
PluginProtos.CodeGeneratorResponse.File.Builder builder = PluginProtos.CodeGeneratorResponse.File.newBuilder();
131132
builder.setName(filename);
132133
builder.setContent(b.toString());

plugin/src/main/java/com/flit/protoc/gen/server/spring/SpringGenerator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.flit.protoc.Parameter;
44
import com.flit.protoc.gen.Generator;
5+
import com.flit.protoc.gen.server.ServiceGenerator;
56
import com.google.protobuf.compiler.PluginProtos;
67

78
import java.util.ArrayList;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package com.flit.protoc.gen.server.undertow;
2+
3+
import com.flit.protoc.gen.server.BaseGenerator;
4+
import com.google.protobuf.DescriptorProtos;
5+
import com.google.protobuf.compiler.PluginProtos;
6+
7+
import java.util.Collections;
8+
import java.util.List;
9+
10+
class RpcGenerator extends BaseGenerator {
11+
12+
private final String filename;
13+
private final String context;
14+
15+
RpcGenerator(DescriptorProtos.FileDescriptorProto proto, DescriptorProtos.ServiceDescriptorProto service, String context) {
16+
super(proto, service);
17+
this.filename = javaPackage.replace(".", "/") + "/Rpc" + this.service.getName() + "Handler.java";
18+
19+
if (context == null) {
20+
this.context = "/twirp";
21+
} else {
22+
context = context.trim();
23+
if (context.equals("")) {
24+
// empty route - i.e. top level "/"
25+
this.context = context;
26+
} else if (context.startsWith("/")) {
27+
this.context = context;
28+
} else {
29+
this.context = "/" + context;
30+
}
31+
}
32+
}
33+
34+
void writeImports() {
35+
// add imports
36+
b.wn("import com.flit.runtime.undertow.ErrorWriter;");
37+
b.wn("import com.google.protobuf.util.JsonFormat;");
38+
b.n();
39+
40+
b.wn("import com.flit.runtime.ErrorCode;");
41+
b.wn("import com.flit.runtime.FlitException;");
42+
b.wn("import io.undertow.server.HttpHandler;");
43+
b.wn("import io.undertow.server.HttpServerExchange;");
44+
b.wn("import io.undertow.util.Headers;");
45+
b.wn("import org.slf4j.Logger;");
46+
b.wn("import org.slf4j.LoggerFactory;");
47+
48+
b.n();
49+
b.wn("import java.io.InputStreamReader;");
50+
b.wn("import java.nio.charset.Charset;");
51+
b.n();
52+
b.wn("import static com.flit.runtime.undertow.FlitHandler.KEY_METHOD;");
53+
b.n();
54+
}
55+
56+
void open(DescriptorProtos.ServiceDescriptorProto s) {
57+
b.wn("public class Rpc", service.getName(), "Handler implements HttpHandler {");
58+
b.n();
59+
60+
// add a logger
61+
b.inc();
62+
b.iwn("private static final Logger LOGGER = LoggerFactory.getLogger(Rpc", service.getName(), "Handler.class);");
63+
64+
// add the static route name
65+
b.iwn("public static final String ROUTE = \"", context, "/",(proto.hasPackage() ? proto.getPackage() + "." : ""), s.getName(), "\";");
66+
b.n();
67+
68+
// add the service handler
69+
b.iwn("private final Rpc", service.getName(), "Service service;");
70+
b.iwn("private final ErrorWriter errorWriter;");
71+
b.n();
72+
73+
// add the constructor for the service
74+
b.iwn("public Rpc", service.getName(), "Handler(Rpc", service.getName() + "Service service) {");
75+
b.inc();
76+
b.iwn("this.service = service;");
77+
b.iwn("this.errorWriter = new ErrorWriter();");
78+
b.dec();
79+
b.iwn("}");
80+
b.n();
81+
82+
}
83+
84+
void close() {
85+
b.dec();
86+
b.wn("}");
87+
}
88+
89+
void writeService(DescriptorProtos.ServiceDescriptorProto s) {
90+
91+
// write the routing table
92+
b.iwn("@Override");
93+
b.iwn("public void handleRequest(HttpServerExchange exchange) throws Exception {");
94+
b.inc();
95+
b.iwn("if (exchange.isInIoThread()) {");
96+
b.inc();
97+
b.iwn("exchange.dispatch(this);");
98+
b.iwn("return;");
99+
b.dec();
100+
b.iwn("}");
101+
b.n();
102+
b.iwn("exchange.startBlocking();");
103+
b.n();
104+
b.iwn("String method = exchange.getAttachment(KEY_METHOD);");
105+
b.n();
106+
b.iwn("try {");
107+
b.inc();
108+
b.iwn("switch(method) {");
109+
b.inc();
110+
s.getMethodList().forEach(m -> b.iwn("case \"", m.getName(), "\": handle", m.getName(), "(exchange); break;"));
111+
b.iwn("default:");
112+
b.inc();
113+
b.iwn("throw FlitException.builder().withErrorCode(ErrorCode.BAD_ROUTE).withMessage(\"No such route\").build();");
114+
b.dec();
115+
116+
b.dec();
117+
b.iwn("}");
118+
b.dec();
119+
b.iwn("} catch (FlitException e) {");
120+
b.inc();
121+
b.iwn("errorWriter.write(e, exchange);");
122+
b.dec();
123+
b.iwn("} catch (Exception e) {");
124+
b.inc();
125+
b.iwn("LOGGER.error(\"Exception caught at handler: error = {}\", e.getMessage(), e);");
126+
b.iwn("errorWriter.write(e, exchange);");
127+
b.dec();
128+
b.iwn("}");
129+
130+
b.dec();
131+
b.iwn("}");
132+
b.n();
133+
134+
s.getMethodList().forEach(m -> {
135+
136+
// the method name
137+
b.iwn("private void handle", m.getName(), "(HttpServerExchange exchange) throws Exception {");
138+
b.inc();
139+
140+
// bind the data
141+
b.iwn("boolean json = false;");
142+
b.iwn(clazz, ".", basename(m.getInputType()), " data;");
143+
b.iwn("if (exchange.getRequestHeaders().get(Headers.CONTENT_TYPE).getFirst().equals(\"application/protobuf\")) {");
144+
b.inc();
145+
b.iwn("data = ", clazz, ".", basename(m.getInputType()), ".parseFrom(exchange.getInputStream());");
146+
b.dec();
147+
b.iwn("} else if (exchange.getRequestHeaders().get(Headers.CONTENT_TYPE).getFirst().startsWith(\"application/json\")) {");
148+
b.inc();
149+
b.iwn("json = true;");
150+
b.iwn(clazz, ".", basename(m.getInputType()), ".Builder builder = ", clazz, ".", basename(m.getInputType()), ".newBuilder();");
151+
b.iwn("JsonFormat.parser().merge(new InputStreamReader(exchange.getInputStream(), Charset.forName(\"UTF-8\")), builder);");
152+
b.iwn("data = builder.build();");
153+
b.dec();
154+
b.iwn("} else {");
155+
b.inc();
156+
b.iwn("exchange.setStatusCode(415);");
157+
b.iwn("return;");
158+
b.dec();
159+
b.iwn("}");
160+
b.n();
161+
162+
// route to the service
163+
b.iwn(clazz, ".", basename(m.getOutputType()), " retval = ", "service.handle", m.getName(), "(data);");
164+
b.iwn("exchange.setStatusCode(200);");
165+
166+
b.iwn("if (json) {");
167+
b.inc();
168+
b.iwn("exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, \"application/json;charset=UTF-8\");");
169+
b.iwn("exchange.getResponseSender().send(JsonFormat.printer().omittingInsignificantWhitespace().print(retval));");
170+
b.iwn("return;");
171+
b.dec();
172+
b.iwn("}");
173+
b.n();
174+
b.iwn("exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, \"application/protobuf\");");
175+
b.iwn("retval.writeTo(exchange.getOutputStream());");
176+
b.dec();
177+
178+
b.iwn("}");
179+
b.n();
180+
});
181+
182+
183+
}
184+
185+
@Override
186+
public List<PluginProtos.CodeGeneratorResponse.File> getFiles() {
187+
PluginProtos.CodeGeneratorResponse.File.Builder builder = PluginProtos.CodeGeneratorResponse.File.newBuilder();
188+
builder.setName(filename);
189+
builder.setContent(b.toString());
190+
191+
return Collections.singletonList(builder.build());
192+
}
193+
}

0 commit comments

Comments
 (0)