ããã¯ããªã«ãããããŠæžãããã®ïŒ
Quarkusã䜿ã£ãŠãFunctionãäœãæ¹æ³ãã¡ãã£ãšèŠãŠãããããªããšæããŸããŠã
ã¬ã€ããèŠãŠãããšãããã€ãéžæè¢ãããããã§ãã
ä»åã¯ãFunqyãšãããã®ãèŠãŠãããããšæããŸãã
FunqyïŒ
Funqyã®ã¬ã€ãã¯ãã¡ãã
Funqyãšããã®ã¯ãQuarkusã®ãµãŒããŒã¬ã¹ã®ã²ãšã€ã§ãããAWS LambdaãAzure FunctionsãGoogle Cloud Functionsã
Knativeãšãã£ããæ§ã
ãªFaaSç°å¢ã«ãããã€ã§ããAPIãšãªãããšãç®æãããã®ã®ããã§ãããŸããã¹ã¿ã³ãã¢ãã³ã§ã
䜿ããŸãã
ãšã¯ããããã®ãããªè€æ°ã®ç°å¢ã«ãŸãããæœè±¡åãè¡ã£ãŠãããããæã£ãŠããæ©èœãéåžžã«åçŽã§ãããä»ã®æ¹æ³ã«æ¯ã¹ãŠ
æ©èœçã«äžè¶³ããããšãããããã§ããå察ã«ãå¯èœãªéãæé©åãããå°ããäœããããã¬ãŒã ã¯ãŒã¯ã§ããããŸãã
ãã以å€ã®ç¹åŸŽãšããŠã¯ã以äžã®ãããªãã®ããããŸãã
@Funq
ãä»äžããã¡ãœãããFunctionãšããŠæ±ã- ãªã¢ã¯ãã£ãïŒSmallRye MutinyïŒããµããŒã
- DIïŒCDIãŸãã¯Spring DIïŒãå©çšå¯
Funqyèªäœã¯ãåFaaSç°å¢ã«ãããã€ã§ãããã®ãå«ãã以äžã§éçºãããŠããããã§ãã
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/extensions/funqy
å ±ééšåã¯ãã¡ãã§ããã
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/extensions/funqy/funqy-server-common
ãããŠãä»åã¯ã¹ã¿ã³ãã¢ãã³ã§äœ¿ãFunqy HTTPã䜿ã£ãŠã¿ãããšæããŸãã
Quarkus - Funqy HTTP Binding (Standalone)
Funqy HTTP
Funqy HTTPã¯ãFunqyãã¹ã¿ã³ãã¢ãã³ã«ãããã€ããŠHTTPã䜿ã£ãŠFunctionãåŒã³åºãä»çµã¿ã«ãªããŸãã
Quarkus - Funqy HTTP Binding (Standalone)
ãœãŒã¹ã³ãŒãã¯ã以äžã§ããã
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/extensions/funqy/funqy-http
unqy HTTPãFunqyã䜿ã£ãŠäœããããåãã©ãããã©ãŒã åãã®Extensionãããããã§ãã
Quarkus - Funqy HTTP Binding with Amazon Lambda
Quarkus - Funqy HTTP Binding with Azure Functions
Quarkus - Funqy HTTP Binding with Google Cloud Functions
Quarkus - Funqy Amazon Lambda Binding
Quarkus - Funqy Google Cloud Functions
Quarkus - Funqy Knative Events Binding
ãšã¯ãããç¹ã«HTTPã®ãã®ãèŠãŠãããšããªã¯ãšã¹ããã©ã¡ãŒã¿ãŒãšããŠQueryStringãJSONãåãåãããšãã§ãããã®ã®
REST APIãšããŠã®æ©èœãHTTPã«é¢ãããµããŒãã¯ä»ã®æ¹æ³ã«æ¯ã¹ããšå£ããšåã¬ã€ãã«ã¯æžãããŠããŸãã
ãããåé¡ã«ãªãå Žåã¯ãåç°å¢ã«é©ããFunctionã®ãµããŒããå©çšãã¹ããªããã§ãã
Quarkus - Amazon Lambda with RESTEasy, Undertow, or Vert.x Web
Quarkus - Azure Functions (Serverless) with RESTEasy, Undertow, or Vert.x Web
Quarkus - Google Cloud Functions (Serverless)
Quarkus - Google Cloud Functions (Serverless) with RESTEasy, Undertow, or Vert.x Web
ãšãã£ãŠããããããpreviewãŸãã¯experimentalãªç¶æ ã§ããã©ãã
説æã¯ãããããã«ããŠãFunqy HTTPãè©ŠããŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã§ãã
$ java --version openjdk 11.0.10 2021-01-19 OpenJDK Runtime Environment (build 11.0.10+9-Ubuntu-0ubuntu1.20.04) OpenJDK 64-Bit Server VM (build 11.0.10+9-Ubuntu-0ubuntu1.20.04, mixed mode, sharing) $ mvn --version Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 11.0.10, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-66-generic", arch: "amd64", family: "unix"
Quarkusã¯ã1.12.1.Finalã䜿çšããŸãã
ãããžã§ã¯ããäœæãã
Funqy HTTPã䜿ããããžã§ã¯ããäœæããŸããExtensionãšããŠã¯ãfunqy-http
ãæå®ããã°OKã§ãã
$ mvn io.quarkus:quarkus-maven-plugin:1.12.1.Final:create \ -DprojectGroupId=org.littlewings \ -DprojectArtifactId=funqy-http-getting-started \ -DprojectVersion=0.0.1-SNAPSHOT \ -Dextensions="funqy-http"
éžæãããExtensionããã³Codestartsã
----------- selected extensions: - io.quarkus:quarkus-funqy-http applying codestarts... ð java 𧰠maven ð quarkus ð config-properties ð dockerfiles ð maven-wrapper ð funqy-http-example
å®äºãããããããžã§ã¯ãå ãžç§»åã
$ cd funqy-http-getting-started
äžèº«ã¯ããããªã£ãŠããŸãã
$ tree -a . âââ .dockerignore âââ .gitignore âââ .mvn â  âââ wrapper â  âââ MavenWrapperDownloader.java â  âââ maven-wrapper.properties âââ README.md âââ mvnw âââ mvnw.cmd âââ pom.xml âââ src âââ main â  âââ docker â  â  âââ Dockerfile.jvm â  â  âââ Dockerfile.legacy-jar â  â  âââ Dockerfile.native â  â  âââ Dockerfile.native-distroless â  âââ java â  â  âââ org â  â  âââ littlewings â  â  âââ funqy â  â  âââ Funqy.java â  âââ resources â  âââ META-INF â  â  âââ resources â  â  âââ index.html â  âââ application.properties âââ test âââ java âââ org âââ littlewings âââ funqy âââ FunqyIT.java âââ FunqyTest.java 17 directories, 17 files
MavenäŸåé¢ä¿ã
<dependencies> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-funqy-http</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-arc</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-junit5</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.rest-assured</groupId> <artifactId>rest-assured</artifactId> <scope>test</scope> </dependency> </dependencies>
ããå°ã詳ããèŠãŠã¿ãŸãããã
$ mvn dependency:tree -Dscope=compile
ãããªããŸããã
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ funqy-http-getting-started --- [INFO] org.littlewings:funqy-http-getting-started:jar:0.0.1-SNAPSHOT [INFO] +- io.quarkus:quarkus-funqy-http:jar:1.12.1.Final:compile [INFO] | +- io.quarkus:quarkus-vertx-http:jar:1.12.1.Final:compile [INFO] | | +- io.quarkus:quarkus-security-runtime-spi:jar:1.12.1.Final:compile [INFO] | | +- io.quarkus:quarkus-vertx-http-dev-console-runtime-spi:jar:1.12.1.Final:compile [INFO] | | +- io.quarkus.security:quarkus-security:jar:1.1.3.Final:compile [INFO] | | +- io.quarkus:quarkus-vertx-core:jar:1.12.1.Final:compile [INFO] | | | +- io.quarkus:quarkus-netty:jar:1.12.1.Final:compile [INFO] | | | | +- io.netty:netty-codec:jar:4.1.49.Final:compile [INFO] | | | | \- io.netty:netty-handler:jar:4.1.49.Final:compile [INFO] | | | \- io.vertx:vertx-core:jar:3.9.5:compile [INFO] | | | +- io.netty:netty-common:jar:4.1.49.Final:compile [INFO] | | | +- io.netty:netty-buffer:jar:4.1.49.Final:compile [INFO] | | | +- io.netty:netty-transport:jar:4.1.49.Final:compile [INFO] | | | +- io.netty:netty-handler-proxy:jar:4.1.49.Final:compile [INFO] | | | | \- io.netty:netty-codec-socks:jar:4.1.49.Final:compile [INFO] | | | +- io.netty:netty-codec-http:jar:4.1.49.Final:compile [INFO] | | | +- io.netty:netty-codec-http2:jar:4.1.49.Final:compile [INFO] | | | +- io.netty:netty-resolver:jar:4.1.49.Final:compile [INFO] | | | \- io.netty:netty-resolver-dns:jar:4.1.49.Final:compile [INFO] | | | \- io.netty:netty-codec-dns:jar:4.1.49.Final:compile [INFO] | | \- io.vertx:vertx-web:jar:3.9.5:compile [INFO] | | +- io.vertx:vertx-web-common:jar:3.9.5:compile [INFO] | | +- io.vertx:vertx-auth-common:jar:3.9.5:compile [INFO] | | \- io.vertx:vertx-bridge-common:jar:3.9.5:compile [INFO] | +- io.quarkus:quarkus-funqy-server-common:jar:1.12.1.Final:compile [INFO] | | \- io.smallrye.reactive:mutiny:jar:0.13.0:compile [INFO] | | +- org.reactivestreams:reactive-streams:jar:1.0.3:compile [INFO] | | \- io.smallrye.common:smallrye-common-annotation:jar:1.5.0:compile [INFO] | \- io.quarkus:quarkus-jackson:jar:1.12.1.Final:compile [INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.1:compile [INFO] | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.1:compile [INFO] | | \- com.fasterxml.jackson.core:jackson-core:jar:2.12.1:compile [INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.12.1:compile [INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.12.1:compile [INFO] | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.12.1:compile [INFO] \- io.quarkus:quarkus-arc:jar:1.12.1.Final:compile [INFO] +- io.quarkus.arc:arc:jar:1.12.1.Final:compile [INFO] | +- jakarta.enterprise:jakarta.enterprise.cdi-api:jar:2.0.2:compile [INFO] | | +- jakarta.el:jakarta.el-api:jar:3.0.3:compile [INFO] | | \- jakarta.interceptor:jakarta.interceptor-api:jar:1.2.5:compile [INFO] | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile [INFO] | +- jakarta.transaction:jakarta.transaction-api:jar:1.3.3:compile [INFO] | \- org.jboss.logging:jboss-logging:jar:3.4.1.Final:compile [INFO] +- io.quarkus:quarkus-core:jar:1.12.1.Final:compile [INFO] | +- jakarta.inject:jakarta.inject-api:jar:1.0:compile [INFO] | +- io.quarkus:quarkus-ide-launcher:jar:1.12.1.Final:compile [INFO] | +- io.quarkus:quarkus-development-mode-spi:jar:1.12.1.Final:compile [INFO] | +- io.smallrye.config:smallrye-config:jar:1.10.2:compile [INFO] | | +- io.smallrye.config:smallrye-config-common:jar:1.10.2:compile [INFO] | | | \- org.eclipse.microprofile.config:microprofile-config-api:jar:1.4:compile [INFO] | | +- io.smallrye.common:smallrye-common-expression:jar:1.5.0:compile [INFO] | | | \- io.smallrye.common:smallrye-common-function:jar:1.5.0:compile [INFO] | | +- io.smallrye.common:smallrye-common-constraint:jar:1.5.0:compile [INFO] | | \- io.smallrye.common:smallrye-common-classloader:jar:1.5.0:compile [INFO] | +- org.jboss.logmanager:jboss-logmanager-embedded:jar:1.0.6:compile [INFO] | +- org.jboss.logging:jboss-logging-annotations:jar:2.2.0.Final:compile [INFO] | +- org.jboss.threads:jboss-threads:jar:3.2.0.Final:compile [INFO] | +- org.slf4j:slf4j-api:jar:1.7.30:compile [INFO] | +- org.jboss.slf4j:slf4j-jboss-logmanager:jar:1.1.0.Final:compile [INFO] | +- org.graalvm.sdk:graal-sdk:jar:21.0.0:compile [INFO] | +- org.wildfly.common:wildfly-common:jar:1.5.4.Final-format-001:compile [INFO] | \- io.quarkus:quarkus-bootstrap-runner:jar:1.12.1.Final:compile [INFO] \- org.eclipse.microprofile.context-propagation:microprofile-context-propagation-api:jar:1.0.1:compile
HTTPã«é¢ããŠã¯Vert.xã䜿ããJSONã¯Jacksonã䜿ããããªæãã§ããã
ãœãŒã¹ã³ãŒããèŠãŠã¿ãŸããããsrc/main/java
åŽã
src/main/java/org/littlewings/funqy/Funqy.java
package org.littlewings.funqy; import io.quarkus.funqy.Funq; import java.util.Random; public class Funqy { private static final String CHARM_QUARK_SYMBOL = "c"; @Funq public String charm(Answer answer) { return CHARM_QUARK_SYMBOL.equalsIgnoreCase(answer.value) ? "You Quark!" : "ð» Wrong answer"; } public static class Answer { public String value; } }
ãã¹ãã³ãŒãã
src/test/java/org/littlewings/funqy/FunqyTest.java
package org.littlewings.funqy; import io.quarkus.test.junit.QuarkusTest; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.containsString; @QuarkusTest public class FunqyTest { @Test public void testCharm() { given() .contentType("application/json") .body("{\"value\": \"c\"}") .post("/charm") .then() .statusCode(200) .body(containsString("You Quark!")); } }
src/test/java/org/littlewings/funqy/FunqyIT.java
package org.littlewings.funqy; import io.quarkus.test.junit.NativeImageTest; @NativeImageTest public class FunqyIT extends FunqyTest { // Run the same tests }
åãããŠã¿ã
1床ããã®ãŸãŸåãããŠã¿ãŸããããããã±ãŒãžã³ã°ã
$ mvn package
èµ·åã
$ java -jar target/quarkus-app/quarkus-run.jar
ããŒã8080ã䜿ã£ãŠQuarkusãèµ·åããã®ã§ãã¢ã¯ã»ã¹ããŠã¿ãŸãã
$ curl localhost:8080/charm -d '{"value": "c"}' "You Quark!" $ curl localhost:8080/charm "ð» Wrong answer"
URLã®ãã¹ã¯ãã¡ãœããåãåæ ãããŸããäœæããããããžã§ã¯ãå
ã®ã¡ãœããåã¯ãcharm
ã ã£ãã®ã§
http://localhost:8080/charm
ã§ã¢ã¯ã»ã¹ããã§ããŸãããšã
Funqy HTTP / Execute Funqy HTTP functions
é¢æ°åŒã³åºããŸã§ã®ãã¬ãŒã¹ãèŠãŠã¿ãŸããããThread#dumpStack
ãè¿œå ããŠã¿ãŸãã
src/main/java/org/littlewings/funqy/Funqy.java
@Funq public String charm(Answer answer) { Thread.dumpStack(); return CHARM_QUARK_SYMBOL.equalsIgnoreCase(answer.value) ? "You Quark!" : "ð» Wrong answer"; }
åŸãããåºåã¯ããããªæãã§ããã»ãšãã©éã«ãªã«ããããŸãããã
java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1388) at org.littlewings.funqy.Funqy.charm(Funqy.java:13) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at io.quarkus.funqy.runtime.FunctionInvoker.invoke(FunctionInvoker.java:120) at io.quarkus.funqy.runtime.bindings.http.VertxRequestHandler.dispatch(VertxRequestHandler.java:141) at io.quarkus.funqy.runtime.bindings.http.VertxRequestHandler.lambda$handle$0(VertxRequestHandler.java:98) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2415) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at java.base/java.lang.Thread.run(Thread.java:834) at org.jboss.threads.JBossThread.run(JBossThread.java:501)
FunqyãFunqy HTTPãããœãŒã¹ã³ãŒããèŠããšãã£ããå°ãããã¬ãŒã ã¯ãŒã¯ãªãã§ãããã
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/extensions/funqy/funqy-server-common
https://github.com/quarkusio/quarkus/tree/1.12.1.Final/extensions/funqy/funqy-http
ã¹ã¬ãããã³ããåã£ãŠã¿ããšãVert.xã®ã¹ã¬ããã®ååšã確èªã§ããŸãã
$ jcmd [PID] Thread.print | grep '^"' "main" #1 prio=5 os_prio=0 cpu=679.82ms elapsed=144.20s tid=0x00007fc5e8016800 nid=0x5e9b waiting on condition [0x00007fc5ec8bd000] "Reference Handler" #2 daemon prio=10 os_prio=0 cpu=0.44ms elapsed=144.17s tid=0x00007fc5e8237000 nid=0x5ea2 waiting on condition [0x00007fc5c01ab000] "Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.46ms elapsed=144.17s tid=0x00007fc5e8239000 nid=0x5ea3 in Object.wait() [0x00007fc5b37fe000] "Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.31ms elapsed=144.17s tid=0x00007fc5e823e800 nid=0x5ea4 runnable [0x0000000000000000] "Service Thread" #5 daemon prio=9 os_prio=0 cpu=0.05ms elapsed=144.17s tid=0x00007fc5e8240800 nid=0x5ea5 runnable [0x0000000000000000] "C2 CompilerThread0" #6 daemon prio=9 os_prio=0 cpu=254.84ms elapsed=144.17s tid=0x00007fc5e8242800 nid=0x5ea6 waiting on condition [0x0000000000000000] "C1 CompilerThread0" #9 daemon prio=9 os_prio=0 cpu=397.30ms elapsed=144.17s tid=0x00007fc5e8244800 nid=0x5ea7 waiting on condition [0x0000000000000000] "Sweeper thread" #10 daemon prio=9 os_prio=0 cpu=0.10ms elapsed=144.17s tid=0x00007fc5e8246800 nid=0x5ea8 runnable [0x0000000000000000] "Common-Cleaner" #11 daemon prio=8 os_prio=0 cpu=0.70ms elapsed=144.15s tid=0x00007fc5e8291000 nid=0x5eaa in Object.wait() [0x00007fc5b2a83000] "vertx-blocked-thread-checker" #16 daemon prio=5 os_prio=0 cpu=9.63ms elapsed=143.57s tid=0x00007fc5e88dd800 nid=0x5eb3 in Object.wait() [0x00007fc5b2481000] "vert.x-eventloop-thread-0" #18 prio=5 os_prio=0 cpu=3.11ms elapsed=143.50s tid=0x00007fc5e8983800 nid=0x5eb4 runnable [0x00007fc5b1b75000] "vert.x-eventloop-thread-1" #19 prio=5 os_prio=0 cpu=2.10ms elapsed=143.50s tid=0x00007fc5e8985000 nid=0x5eb5 runnable [0x00007fc5b1a74000] "vert.x-eventloop-thread-2" #20 prio=5 os_prio=0 cpu=144.07ms elapsed=143.50s tid=0x00007fc5e8987000 nid=0x5eb6 runnable [0x00007fc5b1973000] "vert.x-eventloop-thread-3" #21 prio=5 os_prio=0 cpu=2.90ms elapsed=143.50s tid=0x00007fc5e8988800 nid=0x5eb7 runnable [0x00007fc5b1872000] "vert.x-eventloop-thread-4" #22 prio=5 os_prio=0 cpu=1.47ms elapsed=143.50s tid=0x00007fc5e898a800 nid=0x5eb8 runnable [0x00007fc5b1771000] "vert.x-eventloop-thread-5" #23 prio=5 os_prio=0 cpu=1.91ms elapsed=143.50s tid=0x00007fc5e898c000 nid=0x5eb9 runnable [0x00007fc5b1670000] "vert.x-eventloop-thread-6" #24 prio=5 os_prio=0 cpu=1.56ms elapsed=143.50s tid=0x00007fc5e898e000 nid=0x5eba runnable [0x00007fc5b156f000] "vert.x-eventloop-thread-7" #25 prio=5 os_prio=0 cpu=1.10ms elapsed=143.50s tid=0x00007fc5e898f800 nid=0x5ebb runnable [0x00007fc5b146e000] "vert.x-eventloop-thread-8" #26 prio=5 os_prio=0 cpu=1.66ms elapsed=143.50s tid=0x00007fc5e8991800 nid=0x5ebc runnable [0x00007fc5b136d000] "vert.x-eventloop-thread-9" #27 prio=5 os_prio=0 cpu=2.51ms elapsed=143.50s tid=0x00007fc5e8993800 nid=0x5ebd runnable [0x00007fc5b126c000] "vert.x-eventloop-thread-10" #28 prio=5 os_prio=0 cpu=3.83ms elapsed=143.50s tid=0x00007fc5e8995000 nid=0x5ebe runnable [0x00007fc5b116b000] "vert.x-eventloop-thread-11" #29 prio=5 os_prio=0 cpu=1.38ms elapsed=143.50s tid=0x00007fc5e8997000 nid=0x5ebf runnable [0x00007fc5b106a000] "vert.x-eventloop-thread-12" #30 prio=5 os_prio=0 cpu=1.56ms elapsed=143.50s tid=0x00007fc5e8998800 nid=0x5ec0 runnable [0x00007fc5b0f69000] "vert.x-eventloop-thread-13" #31 prio=5 os_prio=0 cpu=1.27ms elapsed=143.50s tid=0x00007fc5e899a800 nid=0x5ec1 runnable [0x00007fc5b0e68000] "vert.x-eventloop-thread-14" #32 prio=5 os_prio=0 cpu=5.78ms elapsed=143.50s tid=0x00007fc5e899c800 nid=0x5ec2 runnable [0x00007fc5b0d67000] "vert.x-eventloop-thread-15" #33 prio=5 os_prio=0 cpu=1.30ms elapsed=143.50s tid=0x00007fc5e899e800 nid=0x5ec3 runnable [0x00007fc5b0c66000] "vert.x-acceptor-thread-0" #34 prio=5 os_prio=0 cpu=13.89ms elapsed=143.43s tid=0x00007fc55c05c000 nid=0x5ec4 runnable [0x00007fc5b0965000] "executor-thread-2" #35 daemon prio=5 os_prio=0 cpu=8.70ms elapsed=141.73s tid=0x00007fc57c029000 nid=0x5ec8 waiting on condition [0x00007fc5b0664000] "Attach Listener" #36 daemon prio=9 os_prio=0 cpu=1.10ms elapsed=103.47s tid=0x00007fc598001000 nid=0x5f0e waiting on condition [0x0000000000000000] "VM Thread" os_prio=0 cpu=40.89ms elapsed=144.17s tid=0x00007fc5e8234000 nid=0x5ea1 runnable "GC Thread#0" os_prio=0 cpu=19.25ms elapsed=144.19s tid=0x00007fc5e802f800 nid=0x5e9c runnable "GC Thread#1" os_prio=0 cpu=17.08ms elapsed=143.64s tid=0x00007fc5a8001000 nid=0x5eae runnable "GC Thread#2" os_prio=0 cpu=17.22ms elapsed=143.64s tid=0x00007fc5a8002800 nid=0x5eaf runnable "GC Thread#3" os_prio=0 cpu=17.28ms elapsed=143.64s tid=0x00007fc5a8004000 nid=0x5eb0 runnable "GC Thread#4" os_prio=0 cpu=17.62ms elapsed=143.64s tid=0x00007fc5a8005800 nid=0x5eb1 runnable "GC Thread#5" os_prio=0 cpu=17.36ms elapsed=143.64s tid=0x00007fc5a8007000 nid=0x5eb2 runnable "G1 Main Marker" os_prio=0 cpu=0.42ms elapsed=144.19s tid=0x00007fc5e808d800 nid=0x5e9d runnable "G1 Conc#0" os_prio=0 cpu=0.10ms elapsed=144.19s tid=0x00007fc5e808f800 nid=0x5e9e runnable "G1 Refine#0" os_prio=0 cpu=0.31ms elapsed=144.18s tid=0x00007fc5e818f000 nid=0x5e9f runnable "G1 Young RemSet Sampling" os_prio=0 cpu=32.18ms elapsed=144.18s tid=0x00007fc5e8191000 nid=0x5ea0 runnable "VM Periodic Task Thread" os_prio=0 cpu=119.81ms elapsed=144.16s tid=0x00007fc5e828f000 nid=0x5ea9 waiting on condition
å°ãã¯å®è¡ç°å¢ã®é°å²æ°ã¯ããã£ãããªãšæããŸãã
ããã§ãçæããããœãŒã¹ã³ãŒãããã³ãã¹ãã³ãŒããåé€ããŠãèªåã§ãœãŒã¹ã³ãŒããæžããŠãããŸãããã
$ rm src/main/java/org/littlewings/funqy/Funqy.java src/test/java/org/littlewings/funqy/Funqy*
èªåã§FunctionãæžããŠã¿ã
次ã¯ãèªåã§FunctionãæžããŠã¿ãŸãããã
ãã¡ããèŠãŠé²ããŠãããŸãã
Quarkus - Funqy HTTP Binding (Standalone)
以éããœãŒã¹ã³ãŒãã®çŽ¹ä»ããšã«ä»¥äžã®ã³ãã³ããå®è¡ããŠç¢ºèªããŠãããã®ãšããŸãã
$ mvn package $ java -jar target/quarkus-app/quarkus-run.jar
ãŸããSmallRye Mutinyã䜿ã£ãŠé¢æ°ã¯äœæããŸãã
ãŸãã¯ããããªã¯ã©ã¹ãäœæã
src/main/java/org/littlewings/funqy/function/MyFunctions.java
package org.littlewings.funqy.function; import io.quarkus.funqy.Funq; import io.smallrye.mutiny.Uni; public class MyFunctions { @Funq public Uni<String> hello() { return Uni.createFrom().item("Hello Funqy!!"); } @Funq("MyFunction") public Uni<String> annotationSpecFunction() { return Uni.createFrom().item("annotation spec function name"); } }
é¢æ°ã«ã¯@Funqy
ã¢ãããŒã·ã§ã³ãä»äžããŸããã¬ã€ããæ¯ãè¿ããšãé¢æ°ã«ã¢ã¯ã»ã¹ããéã¯ããã©ã«ãã§ã¯
@Funqy
ã¢ãããŒã·ã§ã³ãä»äžããã¡ãœããåããã¹ã«åæ ããããšãã話ã§ããã
Funqy HTTP / Execute Funqy HTTP functions
ãšããããã§ããã¡ãã®ã¡ãœããã«ã¯/hello
ãšãããã¹ã§ã¢ã¯ã»ã¹ã§ããã¯ãã§ãã
@Funq public Uni<String> hello() { return Uni.createFrom().item("Hello Funqy!!"); }
確èªã
$ curl -i localhost:8080/hello HTTP/1.1 200 OK Content-Type: application/json content-length: 15 "Hello Funqy!!"
ãŸãã@Funqy
ã¢ãããŒã·ã§ã³ã«å€ãæå®ããããšã§ããã®ååãé¢æ°åãšããããšãã§ããŸãã
@Funq("MyFunction") public Uni<String> annotationSpecFunction() { return Uni.createFrom().item("annotation spec function name"); }
確èªã
$ curl -i localhost:8080/MyFunction HTTP/1.1 200 OK Content-Type: application/json content-length: 31 "annotation spec function name"
ãªããé¢æ°åã¯å€§æåã»å°æåãåºå¥ããŸãã
$ curl -i localhost:8080/myfunction HTTP/1.1 404 Not Found content-type: text/html; charset=utf-8 content-length: 53 <html><body><h1>Resource not found</h1></body></html>
次ã¯ããã©ã¡ãŒã¿ãŒãåãåãããã«ããŠã¿ãŸããããGETã®å Žåã¯QueryStringããPOSTã®å Žåã¯HTTP Bodyãåãåãããšã
ã§ããŸãã
Funqy HTTP / GET Query Parameter Mapping
src/main/java/org/littlewings/funqy/function/ParameterFunctions.java
package org.littlewings.funqy.function; import java.util.Map; import io.quarkus.funqy.Funq; import io.smallrye.mutiny.Uni; public class ParameterFunctions { @Funq public Uni<String> map(Map<String, String> request) { return Uni .createFrom() .item(String.format("Hello[map], %s %s: %s", request.get("firstName"), request.get("lastName"), request.get("age"))); } @Funq public Uni<Result> bean(Person p) { return Uni .createFrom() .item(new Result("bean", String.format("%s %s: %d", p.getFirstName(), p.getLastName(), p.age))); } }
ãã©ã¡ãŒã¿ãŒã¯Map
ã§åãåãããJavaãªããžã§ã¯ããšããŠåãåãããšãå¯èœã§ãã
2ã€ç®ã®ã¡ãœããã¯Javaãªããžã§ã¯ããåãåãããã«ããŠãããŸããããã®å®çŸ©ã¯ãã¡ãã
src/main/java/org/littlewings/funqy/function/Person.java
package org.littlewings.funqy.function; public class Person { String firstName; String lastName; int age; ãgetterïŒsetterã¯çç¥ã }
ãŸããã¬ã¹ãã³ã¹ãèªåã§å®çŸ©ããã¯ã©ã¹ã«ããŠãããŸãã
src/main/java/org/littlewings/funqy/function/Result.java
package org.littlewings.funqy.function; public class Result { String message; public Result(String prefix, String message) { this.message = String.format("Hello[%s] %s", prefix, message); } public String getMessage() { return message; } }
ãŸãã¯ãMap
ã§åãåãæ¹ãã確èªã
@Funq public Uni<String> map(Map<String, String> request) { return Uni .createFrom() .item(String.format("Hello[map], %s %s: %s", request.get("firstName"), request.get("lastName"), request.get("age"))); }
çµæã
$ curl -i 'localhost:8080/map?firstName=katsuo&lastName=isono&age=11' HTTP/1.1 200 OK Content-Type: application/json content-length: 30 "Hello[map], katsuo isono: 11"
QueryStringã§æž¡ããããã©ã¡ãŒã¿ãŒããJavaãªããžã§ã¯ãã«ãããã³ã°ããŠåãåãããšãã§ããŸãã
$ curl -i 'localhost:8080/bean?firstName=katsuo&lastName=isono&age=11' HTTP/1.1 200 OK Content-Type: application/json content-length: 42 {"message":"Hello[bean] katsuo isono: 11"}
ãã¡ãã¯ãã¬ã¹ãã³ã¹ããªããžã§ã¯ã圢åŒã«ãªããŸããã
次ã¯ãPOSTã§JSONãéä¿¡ããŠã¿ãŸãããã
@Funq public Uni<Result> bean(Person p) { return Uni .createFrom() .item(new Result("bean", String.format("%s %s: %d", p.getFirstName(), p.getLastName(), p.age))); }
ãã¡ãããMap
ããã³Javaãªããžã§ã¯ãã®ã©ã¡ãã§ãåãåããŸãã
$ curl -i -XPOST -H 'Content-Type:application/json' localhost:8080/map -d '{"firstName": "ã«ããª", "lastName": "磯é", "age": 11}' HTTP/1.1 200 OK Content-Type: application/json content-length: 34 "Hello[map], ã«ã㪠磯é: 11" $ curl -i -XPOST -H 'Content-Type:application/json' localhost:8080/bean -d '{"firstName": "ã«ããª", "lastName": "磯é", "age": 11}' HTTP/1.1 200 OK Content-Type: application/json content-length: 46 {"message":"Hello[bean] ã«ã㪠磯é: 11"}
æåŸã¯ãDIã䜿ããŸããããä»åã¯CDIã䜿ããŸãã
ãªã®ã§ããããŸãã¯ãã®åã«é¢æ°ãæã£ãã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãã©ãããã©ã€ããµã€ã¯ã«ã«ãªã£ãŠãããç¥ããããšããã
ã¬ã€ãã«ãããšãããã©ã«ãã§ã¯@Dependent
ãšåçã ããã§ãã
The default object lifecycle for a Funqy class is @Dependent.
æåã«äœã£ãã¯ã©ã¹ã®ã³ã³ã¹ãã©ã¯ã¿ã«ãThread#dumpStack
ãä»èŸŒãã§ç¢ºèªããŠã¿ãŸãã
public MyFunctions() {
Thread.dumpStack();
}
ãããšãcurl
ã§ã®ã¢ã¯ã»ã¹ããšã«ã¹ã¿ãã¯ãã¬ãŒã¹ãåºåãããŸãã
java.lang.Exception: Stack trace at java.base/java.lang.Thread.dumpStack(Thread.java:1388) at org.littlewings.funqy.function.MyFunctions.<init>(MyFunctions.java:8) at org.littlewings.funqy.function.MyFunctions_Bean.create(MyFunctions_Bean.zig:104) at org.littlewings.funqy.function.MyFunctions_Bean.get(MyFunctions_Bean.zig:134) at org.littlewings.funqy.function.MyFunctions_Bean.get(MyFunctions_Bean.zig:169) at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:430) at io.quarkus.arc.impl.ArcContainerImpl.beanInstanceHandle(ArcContainerImpl.java:443) at io.quarkus.arc.impl.ArcContainerImpl$1.get(ArcContainerImpl.java:266) at io.quarkus.arc.impl.ArcContainerImpl$1.get(ArcContainerImpl.java:263) at io.quarkus.arc.runtime.BeanContainerImpl$1.create(BeanContainerImpl.java:35) at io.quarkus.funqy.runtime.FunctionConstructor.construct(FunctionConstructor.java:18) at io.quarkus.funqy.runtime.FunctionInvoker.invoke(FunctionInvoker.java:118) at io.quarkus.funqy.runtime.bindings.http.VertxRequestHandler.dispatch(VertxRequestHandler.java:141) at io.quarkus.funqy.runtime.bindings.http.VertxRequestHandler.lambda$handle$0(VertxRequestHandler.java:98) at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:231) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2415) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at java.base/java.lang.Thread.run(Thread.java:834) at org.jboss.threads.JBossThread.run(JBossThread.java:501)
ãªã¯ãšã¹ãããšã«ã€ã³ã¹ã¿ã³ã¹åãããŠããããã§ãã
ãã®ãããã®ãœãŒã¹ã³ãŒãã¯ããã¡ãã
ã§ã¯ã@ApplicationScoped
ã¢ãããŒã·ã§ã³ãä»äžããŠã¿ãŸãã
@ApplicationScoped public class MyFunctions { public MyFunctions() { Thread.dumpStack(); }
ãããšããã«ãã«å€±æããããã«ãªããŸãâŠãäºéã§ã¹ã³ãŒãå®çŸ©ãå ¥ã£ãæãã«ãªã£ãŠããããã§ããâŠã
[ERROR] [error]: Build step io.quarkus.arc.deployment.ArcProcessor#registerBeans threw an exception: javax.enterprise.inject.spi.DefinitionException: Bean class org.littlewings.funqy.function.MyFunctions declares multiple scope type annotations: javax.enterprise.context.ApplicationScoped, javax.enterprise.context.Dependent
ãšããããã§ãã¹ã³ãŒãå®çŸ©ã¯æ瀺çã«å ¥ããªãããšã«ããŸããæ°ãåãçŽããŠã
CDI管çBeanãäœæã
src/main/java/org/littlewings/funqy/service/MessageService.java
package org.littlewings.funqy.service; import javax.enterprise.context.ApplicationScoped; import io.smallrye.mutiny.Uni; @ApplicationScoped public class MessageService { public Uni<String> format(String firstName, String lastName, int age) { return Uni .createFrom() .item(String.format("%s %s: %d", firstName, lastName, age)); } }
ãã¡ãã@Inject
ããŠäœ¿çšãããé¢æ°ãæã£ãã¯ã©ã¹ãäœæã
src/main/java/org/littlewings/funqy/function/CdiFunctions.java
package org.littlewings.funqy.function; import javax.inject.Inject; import io.quarkus.funqy.Funq; import io.smallrye.mutiny.Uni; import org.littlewings.funqy.service.MessageService; public class CdiFunctions { @Inject MessageService messageService; @Funq public Uni<Result> cdi(Person p) { return Uni .combine() .all() .unis( Uni.createFrom().item("cdi"), messageService.format(p.getFirstName(), p.getLastName(), p.getAge()) ) .combinedWith((s1, s2) -> new Result(s1, s2)); } }
ãªã¯ãšã¹ãããã³ã¬ã¹ãã³ã¹ã¯ãå ã»ã©ãã©ã¡ãŒã¿ãŒãæ±ã£ãæã«äœæããã¯ã©ã¹ãå©çšããŠããŸãã
確èªã
$ curl -i -XPOST -H 'Content-Type: application/json' localhost:8080/cdi -d '{"firstName": "ã«ããª", "lastName": "磯é", "age": 11}' HTTP/1.1 200 OK Content-Type: application/json content-length: 45 {"message":"Hello[cdi] ã«ã㪠磯é: 11"}
OKã§ããã
ããšãè©ŠããŠããªãæ©èœãšããŠã¯Context Injectionãããã®ã§ãããä»åã¯ãã¹ã
ãªããšãªãã䜿ãæ¹ãé°å²æ°ã¯ããããŸããã
ãã¹ãã³ãŒã
æåŸã«ããããŸã§curl
ã§ç¢ºèªããŠããå
容ãšåãããšããããã¹ãã³ãŒããèŒããŠãããŸãã
src/test/java/org/littlewings/funqy/function/MyFunctionsTest.java
package org.littlewings.funqy.function; import io.quarkus.test.junit.QuarkusTest; import org.apache.http.entity.ContentType; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; @QuarkusTest class MyFunctionsTest { @Test public void helloTest() { given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .when() .get("/hello") .then() .assertThat() .statusCode(200) .body(is("\"Hello Funqy!!\"")); } @Test public void annotationSpecFunctionTest() { given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .when() .get("/MyFunction") .then() .assertThat() .statusCode(200) .body(is("\"annotation spec function name\"")); } @Test public void caseSensitiveTest() { given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .get("/myfunction") .then() .assertThat() .statusCode(404); } }
src/test/java/org/littlewings/funqy/function/ParameterFunctionsTest.java
package org.littlewings.funqy.function; import io.quarkus.test.junit.QuarkusTest; import org.apache.http.entity.ContentType; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; @QuarkusTest class ParameterFunctionsTest { @Test public void mapQueryTest() { given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .queryParam("firstName", "ã«ããª") .queryParam("lastName", "磯é") .queryParam("age", 11) .when() .get("/map") .then() .assertThat() .statusCode(200) .body(is("\"Hello[map], ã«ã㪠磯é: 11\"")); } @Test public void beanQueryTest() { given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .queryParam("firstName", "ã«ããª") .queryParam("lastName", "磯é") .queryParam("age", 11) .when() .get("/bean") .then() .assertThat() .statusCode(200) .body("message", is("Hello[bean] ã«ã㪠磯é: 11")); } @Test public void mapJsonTest() { Person request = new Person(); request.setFirstName("ã«ããª"); request.setLastName("磯é"); request.setAge(11); given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .body(request) .when() .post("/map") .then() .assertThat() .statusCode(200) .body(is("\"Hello[map], ã«ã㪠磯é: 11\"")); } @Test public void beanTest() { Person request = new Person(); request.setFirstName("ã«ããª"); request.setLastName("磯é"); request.setAge(11); given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .body(request) .when() .post("/bean") .then() .assertThat() .statusCode(200) .body("message", is("Hello[bean] ã«ã㪠磯é: 11")); } }
src/test/java/org/littlewings/funqy/function/CdiFunctionsTest.java
package org.littlewings.funqy.function; import io.quarkus.test.junit.QuarkusTest; import org.apache.http.entity.ContentType; import org.junit.jupiter.api.Test; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; @QuarkusTest class CdiFunctionsTest { @Test public void cdiTest() { Person request = new Person(); request.setFirstName("ã«ããª"); request.setLastName("磯é"); request.setAge(11); given() .contentType(ContentType.APPLICATION_JSON.getMimeType()) .body(request) .when() .post("/cdi") .then() .assertThat() .statusCode(200) .body("message", is("Hello[cdi] ã«ã㪠磯é: 11")); } }