Skip to content

Commit 9aeb15e

Browse files
authored
Merge pull request #91 from avaje/feature/nima
Helidon Nima support initial
2 parents 0cd16f1 + 6c90974 commit 9aeb15e

File tree

32 files changed

+1504
-90
lines changed

32 files changed

+1504
-90
lines changed

.github/workflows/build.yml

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,41 @@
11
name: Build
22

33
on: [push, pull_request, workflow_dispatch]
4-
54
jobs:
65
build:
7-
86
runs-on: ${{ matrix.os }}
97
permissions:
108
contents: read
119
packages: write
1210
strategy:
1311
fail-fast: false
1412
matrix:
15-
java_version: [11,17]
13+
java_version: [11, 17, 19]
1614
os: [ubuntu-latest]
1715

1816
steps:
19-
- uses: actions/checkout@v2
20-
- name: Set up Java
21-
uses: actions/setup-java@v2
22-
with:
23-
java-version: ${{ matrix.java_version }}
24-
distribution: 'zulu'
25-
- name: Maven cache
26-
uses: actions/cache@v2
27-
env:
28-
cache-name: maven-cache
29-
with:
30-
path:
31-
~/.m2
32-
key: build-${{ env.cache-name }}
33-
- name: Maven version
34-
run: mvn --version
35-
- name: Build with Maven
36-
run: mvn clean test
17+
- uses: actions/checkout@v2
18+
- name: Set up Java
19+
uses: actions/setup-java@v2
20+
with:
21+
java-version: ${{ matrix.java_version }}
22+
distribution: "zulu"
23+
- name: Maven cache
24+
uses: actions/cache@v2
25+
env:
26+
cache-name: maven-cache
27+
with:
28+
path: ~/.m2
29+
key: build-${{ env.cache-name }}
30+
- name: Maven version
31+
run: mvn --version
32+
- name: Build with Maven
33+
env:
34+
JAVA_VERSION: ${{ matrix.java_version }}
35+
run: |
36+
if (( JAVA_VERSION < 19 ));
37+
then
38+
mvn clean test -pl "!:avaje-http-nima-generator"
39+
else
40+
mvn clean test
41+
fi

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,9 @@ build/
33
.idea/
44
*.iml
55
.gradle
6+
*.prefs
7+
*.class*
8+
*.factorypath
9+
*.project
10+
*.processors
11+
*/bin/

README.md

Lines changed: 158 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,79 +6,95 @@ Http server and client libraries and code generation.
66

77
A jax-rs style controllers with annotations (`@Path`, `@Get` ...)
88
that is lightweight by using source code generation (annotation processors)
9-
to generate adapter code for Javalin and Helidon SE.
9+
to generate adapter code for Javalin and Helidon SE/Nima.
1010

1111
- Lightweight as in 65Kb library + generated source code
12-
- Full use of Javalin or Helidon SE as desired
12+
- Full use of Javalin or Helidon SE/Nima as desired
1313

1414

15-
## Define a Controller
15+
## Define a Controller (Note that these APT processors works with both Java and Kotlin.)
1616
```java
17-
package org.example.hello
17+
package org.example.hello;
1818

19-
import io.avaje.http.api.Controller
20-
import io.avaje.http.api.Get
21-
import io.avaje.http.api.Path
19+
import io.avaje.http.api.Controller;
20+
import io.avaje.http.api.Get;
21+
import io.avaje.http.api.Path;
22+
import java.util.List;
2223

2324
@Path("/widgets")
2425
@Controller
25-
class WidgetController(private val hello: HelloComponent) {
26+
public class WidgetController {
27+
private final HelloComponent hello;
28+
public WidgetController(HelloComponent hello) {
29+
this.hello = hello;
30+
}
31+
32+
@Get("/{id}")
33+
Widget getById(int id) {
34+
return new Widget(id, "you got it"+ hello.hello());
35+
}
2636

27-
@Get("/:id")
28-
fun getById(id : Int): Widget {
29-
return Widget(id, "you got it${hello.hello()}")
37+
@Get()
38+
List<Widget> getAll() {
39+
return List.of(new Widget(1, "Rob"), new Widget(2, "Fi"));
3040
}
3141

32-
@Get
33-
fun getAll(): MutableList<Widget> {
42+
record Widget(int id, String name){};
43+
}
3444

35-
val list = mutableListOf<Widget>()
36-
list.add(Widget(1, "Rob"))
37-
list.add(Widget(2, "Fi"))
45+
```
3846

39-
return list
40-
}
47+
## Usage with Javalin
4148

42-
data class Widget(var id: Int, var name: String)
43-
}
49+
The annotation processor will generate controller classes implementing the WebRoutes interface, which means we can
50+
get all the WebRoutes and register them with Javalin using:
51+
52+
```java
53+
var routes = BeanScope.builder().build().list(WebRoutes.class);
4454

55+
Javalin.create()
56+
.routes(() -> routes.forEach(WebRoutes::registerRoutes))
57+
.start();
4558
```
4659

47-
## Generated source (Javalin)
4860

49-
The annotation processor will generate a `$Route` for the controller like below.
61+
## Usage with Helidon SE
5062

51-
Note that this class implements the WebRoutes interface, which means we can
52-
get all the WebRoutes and register them with Javalin using.
63+
The annotation processor will generate controller classes implementing the Helidon Service interface, which we can use
64+
get all the Services and register them with Helidon `RoutingBuilder`.
5365

5466
```java
55-
fun main(args: Array<String>) {
56-
57-
// get all the webRoutes
58-
val webRoutes = BeanScope.builder().build().list(WebRoutes::class.java)
67+
var routes = BeanScope.builder().build().list(Service.class);
68+
var routingBuilder = Routing.builder().register(routes.stream().toArray(Service[]::new));
69+
WebServer.builder()
70+
.addMediaSupport(JacksonSupport.create())
71+
.routing(routingBuilder)
72+
.build()
73+
.start();
74+
```
5975

60-
val javalin = Javalin.create()
76+
## Usage with Helidon Nima
6177

62-
javalin.routes {
63-
// register all the routes with Javalin
64-
webRoutes.forEach { it.registerRoutes() }
78+
The annotation processor will generate controller classes implementing the Helidon HttpService interface, which we can use
79+
get all the services and register them with the Helidon `HttpRouting`.
6580

66-
// other routes etc as desired
67-
ApiBuilder.get("/foo") { ctx ->
68-
ctx.html("bar")
69-
ctx.status(200)
70-
}
71-
...
72-
}
81+
```java
82+
var routes = BeanScope.builder().build().list(HttpService.class);
83+
final var builder = HttpRouting.builder();
7384

74-
javalin.start(7000)
85+
for (final HttpService httpService : routes) {
86+
httpService.routing(builder);
7587
}
7688

89+
WebServer.builder()
90+
.addRouting(builder.build())
91+
.build()
92+
.start();
7793
```
94+
## Generated sources
7895

7996
### (Javalin) The generated WidgetController$Route.java is:
8097

81-
8298
```java
8399
package org.example.hello;
84100

@@ -102,7 +118,7 @@ public class WidgetController$Route implements WebRoutes {
102118
@Override
103119
public void registerRoutes() {
104120

105-
ApiBuilder.get("/widgets/:id", ctx -> {
121+
ApiBuilder.get("/widgets/{id}", ctx -> {
106122
int id = asInt(ctx.pathParam("id"));
107123
ctx.json(controller.getById(id));
108124
ctx.status(200);
@@ -117,24 +133,7 @@ public class WidgetController$Route implements WebRoutes {
117133
}
118134
```
119135

120-
## Generated source (Helidon SE)
121-
122-
The annotation processor will generate a `$Route` for the controller like below.
123-
124-
Note that this class implements the Helidon Service interface, which means we can
125-
get all the Services and register them with Helidon `RoutingBuilder`.
126-
127-
```
128-
var routes = BeanScope.builder().build().list(Service.class);
129-
var routingBuilder = Routing.builder().register(routes.stream().toArray(Service[]::new));
130-
WebServer.builder()
131-
.addMediaSupport(JacksonSupport.create())
132-
.routing(routingBuilder)
133-
.build()
134-
.start();
135-
```
136136
### (Helidon SE) The generated WidgetController$Route.java is:
137-
138137
```java
139138
package org.example.hello;
140139

@@ -163,7 +162,7 @@ public class WidgetController$Route implements Service {
163162
@Override
164163
public void update(Routing.Rules rules) {
165164

166-
rules.get("/widgets/:id", this::_getById);
165+
rules.get("/widgets/{id}", this::_getById);
167166
rules.post("/widgets", this::_getAll);
168167
}
169168

@@ -179,6 +178,104 @@ public class WidgetController$Route implements Service {
179178
}
180179
```
181180

182-
Note that this APT processor works with both Java and Kotlin.
181+
### (Helidon Nima) The generated WidgetController$Route.java is:
182+
183+
```java
184+
package org.example.hello;
185+
186+
import static io.avaje.http.api.PathTypeConversion.*;
187+
188+
import io.avaje.http.api.*;
189+
import io.avaje.inject.Component;
190+
import io.helidon.nima.webserver.http.HttpRouting;
191+
import io.helidon.nima.webserver.http.HttpRules;
192+
import io.helidon.nima.webserver.http.HttpService;
193+
import io.helidon.nima.webserver.http.ServerRequest;
194+
import io.helidon.nima.webserver.http.ServerResponse;
195+
import org.example.hello.WidgetController;
196+
197+
@Generated("avaje-helidon-nima-generator")
198+
@Component
199+
public class WidgetController$Route implements HttpService {
200+
201+
private final WidgetController controller;
202+
public WidgetController$Route(WidgetController controller) {
203+
this.controller = controller;
204+
}
205+
206+
@Override
207+
public void routing(HttpRules rules) {
208+
rules.get("/widgets/{id}", this::_getById);
209+
rules.get("/widgets", this::_getAll);
210+
}
211+
212+
private void _getById(ServerRequest req, ServerResponse res) {
213+
var pathParams = req.path().pathParameters();
214+
int id = asInt(pathParams.first("id").get());
215+
var result = controller.getById(id);
216+
res.send(result);
217+
}
218+
219+
private void _getAll(ServerRequest req, ServerResponse res) {
220+
var pathParams = req.path().pathParameters();
221+
var result = controller.getAll();
222+
res.send(result);
223+
}
224+
225+
}
226+
```
227+
228+
### (Helidon Nima with Avaje-Jsonb) The generated WidgetController$Route.java is:
183229

230+
```java
231+
package org.example.hello;
184232

233+
import static io.avaje.http.api.PathTypeConversion.*;
234+
235+
import io.avaje.http.api.*;
236+
import io.avaje.inject.Component;
237+
import io.helidon.nima.webserver.http.HttpRouting;
238+
import io.helidon.nima.webserver.http.HttpRules;
239+
import io.helidon.nima.webserver.http.HttpService;
240+
import io.helidon.nima.webserver.http.ServerRequest;
241+
import io.helidon.nima.webserver.http.ServerResponse;
242+
import org.example.hello.WidgetController;
243+
244+
@Generated("avaje-helidon-nima-generator")
245+
@Component
246+
public class WidgetController$Route implements HttpService {
247+
248+
249+
private final WidgetController controller;
250+
private final JsonType<org.example.hello.WidgetController.Widget> getByIdReturnedJsonType;
251+
private final JsonType<java.util.List<org.example.hello.WidgetController.Widget>> getAllReturnedJsonType;
252+
253+
public WidgetController$Route(WidgetController controller, Jsonb jsonB) {
254+
this.controller = controller;
255+
this.getByIdReturnedJsonType = jsonB.type(org.example.hello.WidgetController.Widget.class);
256+
this.getAllReturnedJsonType = jsonB.type(org.example.hello.WidgetController.Widget.class).list();
257+
}
258+
259+
@Override
260+
public void routing(HttpRules rules) {
261+
rules.get("/widgets/{id}", this::_getById);
262+
rules.get("/widgets", this::_getAll);
263+
}
264+
265+
private void _getById(ServerRequest req, ServerResponse res) {
266+
var pathParams = req.path().pathParameters();
267+
int id = asInt(pathParams.first("id").get());
268+
var result = controller.getById(id);
269+
res.headers().contentType(io.helidon.common.http.HttpMediaType.APPLICATION_JSON);
270+
getByIdReturnedJsonType.toJson(result, res.outputStream());
271+
}
272+
273+
private void _getAll(ServerRequest req, ServerResponse res) {
274+
var pathParams = req.path().pathParameters();
275+
var result = controller.getAll();
276+
res.headers().contentType(io.helidon.common.http.HttpMediaType.APPLICATION_JSON);
277+
getAllReturnedJsonType.toJson(result, res.outputStream());
278+
}
279+
280+
}
281+
```

http-generator-core/src/main/java/io/avaje/http/generator/core/PathSegments.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ private String fullPath(String prefix, String suffix) {
126126
return chunks.fullPath(prefix, suffix);
127127
}
128128

129+
public boolean isEmpty() {
130+
return segments.isEmpty();
131+
}
132+
129133
public static class Segment {
130134

131135
private final String name;

0 commit comments

Comments
 (0)