-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
15ce207
commit 8ce1d83
Showing
21 changed files
with
560 additions
and
5 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
buildSrc/src/main/java/io/micronaut/guides/feature/MicronautGraalpy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.micronaut.guides.feature; | ||
|
||
import io.micronaut.guides.feature.template.graalpyMavenPluginPackage; | ||
import io.micronaut.starter.application.generator.GeneratorContext; | ||
import io.micronaut.starter.build.BuildProperties; | ||
import io.micronaut.starter.build.dependencies.CoordinateResolver; | ||
import io.micronaut.starter.build.maven.MavenPlugin; | ||
import io.micronaut.starter.options.BuildTool; | ||
import io.micronaut.starter.template.RockerWritable; | ||
import jakarta.inject.Singleton; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
@Singleton | ||
class MicronautGraalpy extends AbstractFeature { | ||
|
||
private static final String GROUP_ID_GRAALVM_PYTHON = "org.graalvm.python"; | ||
private static final String ARTIFACT_ID_GRAALPY_MAVEN_PLUGIN = "graalpy-maven-plugin"; | ||
|
||
private final CoordinateResolver coordinateResolver; | ||
|
||
MicronautGraalpy(CoordinateResolver coordinateResolver) { | ||
super("graalpy", "micronaut-graalpy"); | ||
this.coordinateResolver = coordinateResolver; | ||
} | ||
|
||
@Override | ||
public void apply(GeneratorContext generatorContext) { | ||
super.apply(generatorContext); | ||
if (generatorContext.getBuildTool() == BuildTool.MAVEN) { | ||
BuildProperties props = generatorContext.getBuildProperties(); | ||
coordinateResolver.resolve(ARTIFACT_ID_GRAALPY_MAVEN_PLUGIN) | ||
.ifPresent(coordinate -> props.put("graalpy.maven.plugin.version", coordinate.getVersion())); | ||
generatorContext.addBuildPlugin(graalpyMavenPlugin()); | ||
} | ||
} | ||
|
||
protected MavenPlugin graalpyMavenPlugin() { | ||
return MavenPlugin.builder() | ||
.groupId(GROUP_ID_GRAALVM_PYTHON) | ||
.artifactId(ARTIFACT_ID_GRAALPY_MAVEN_PLUGIN) | ||
.extension(new RockerWritable(graalpyMavenPluginPackage.template(pythonPackages()))) | ||
.build(); | ||
} | ||
|
||
protected List<String> pythonPackages() { | ||
return Collections.emptyList(); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
buildSrc/src/main/java/io/micronaut/guides/feature/MicronautGraalpyPygal.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package io.micronaut.guides.feature; | ||
|
||
import io.micronaut.starter.build.dependencies.CoordinateResolver; | ||
import jakarta.inject.Singleton; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
@Singleton | ||
class MicronautGraalpyPygal extends MicronautGraalpy { | ||
|
||
MicronautGraalpyPygal(CoordinateResolver coordinateResolver) { | ||
super(coordinateResolver); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "graalpy-pygal"; | ||
} | ||
|
||
@Override | ||
protected List<String> pythonPackages() { | ||
return Collections.singletonList("pygal==3.0.4"); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...c/src/main/java/io/micronaut/guides/feature/template/graalpyMavenPluginPackage.rocker.raw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
@import java.util.List | ||
@args (List<String> packages) | ||
<!-- tag::graalpy-maven-plugin[] --> | ||
<plugin> | ||
<groupId>org.graalvm.python</groupId> | ||
<artifactId>graalpy-maven-plugin</artifactId> | ||
<version>${graalpy.maven.plugin.version}</version> | ||
@if(!packages.isEmpty()) { | ||
<configuration> | ||
<packages> | ||
@for (String pkg : packages) { | ||
<package>@(pkg)</package> | ||
} | ||
</packages> | ||
</configuration> | ||
} | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>process-graalpy-resources</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<!-- end::graalpy-maven-plugin[] --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
43 changes: 43 additions & 0 deletions
43
...icronaut-graalpy-python-package/java/src/main/java/example/micronaut/PygalController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* | ||
* Copyright 2017-2024 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package example.micronaut; | ||
|
||
import io.micronaut.http.annotation.Controller; | ||
import io.micronaut.http.annotation.Get; | ||
import io.micronaut.http.annotation.Produces; | ||
import io.micronaut.scheduling.TaskExecutors; | ||
import io.micronaut.scheduling.annotation.ExecuteOn; | ||
|
||
@Controller("/pygal") // <1> | ||
class PygalController { | ||
|
||
private final PygalModule pygal; | ||
|
||
PygalController(PygalModule pygal) { // <2> | ||
this.pygal = pygal; | ||
} | ||
|
||
@ExecuteOn(TaskExecutors.BLOCKING) | ||
@Get // <3> | ||
@Produces("image/svg+xml") // <4> | ||
public String index() { | ||
PygalModule.StackedBar stackedBar = pygal.StackedBar(); // <5> | ||
stackedBar.add("Fibonacci", new int[] {0, 1, 1, 2, 3, 5, 8}); // <6> | ||
PygalModule.Svg svg = stackedBar.render(); // <7> | ||
return svg.decode(); // <8> | ||
} | ||
|
||
} |
17 changes: 17 additions & 0 deletions
17
...es/micronaut-graalpy-python-package/java/src/main/java/example/micronaut/PygalModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package example.micronaut; | ||
|
||
import io.micronaut.graal.graalpy.annotations.GraalPyModule; | ||
|
||
@GraalPyModule("pygal") // <1> | ||
interface PygalModule { | ||
StackedBar StackedBar(); // <2> | ||
|
||
interface StackedBar { // <3> | ||
void add(String title, int[] i); // <4> | ||
Svg render(); // <5> | ||
} | ||
|
||
interface Svg { // <6> | ||
String decode(); // <7> | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
...ut-graalpy-python-package/java/src/main/resources/META-INF/native-image/proxy-config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[ | ||
["example.micronaut.PygalModule"], | ||
["example.micronaut.PygalModule$StackedBar"], | ||
["example.micronaut.PygalModule$Svg"] | ||
] |
38 changes: 38 additions & 0 deletions
38
...naut-graalpy-python-package/java/src/test/java/example/micronaut/PygalControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright 2017-2024 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package example.micronaut; | ||
|
||
import io.micronaut.http.HttpRequest; | ||
import io.micronaut.http.client.HttpClient; | ||
import io.micronaut.http.client.annotation.Client; | ||
import io.micronaut.runtime.server.EmbeddedServer; | ||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest; | ||
import jakarta.inject.Inject; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
@MicronautTest // <1> | ||
class PygalControllerTest { | ||
|
||
@Test | ||
void testPygalResponse(@Client("/") HttpClient client) { // <2> | ||
String response = client.toBlocking().retrieve(HttpRequest.GET("/pygal")); // <3> | ||
assertTrue(response.contains("<svg xmlns:xlink")); | ||
assertTrue(response.contains("<title>Pygal</title>")); | ||
assertTrue(response.contains("<g class=\"graph stackedbar-graph vertical\">")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"title": "Creating a Micronaut GraalPy application using a Python package", | ||
"intro": "Learn how to create a simple Micronaut GraalPy application using a third party Python package.", | ||
"authors": ["Tomas Stupka"], | ||
"tags": ["junit"], | ||
"languages": ["java"], | ||
"buildTools": ["maven"], | ||
"categories": ["Getting Started"], | ||
"minimumJavaVersion": 21, | ||
"publicationDate": "2024-07-01", | ||
"apps": [ | ||
{ | ||
"name": "default", | ||
"invisibleFeatures": ["graalpy-pygal"], | ||
"features": ["graalpy"] | ||
} | ||
] | ||
} |
97 changes: 97 additions & 0 deletions
97
guides/micronaut-graalpy-python-package/micronaut-graalpy-python-package.adoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
common:header.adoc[] | ||
|
||
common:requirements.adoc[] | ||
|
||
common:completesolution.adoc[] | ||
|
||
common:create-app-features.adoc[] | ||
|
||
=== GraalPy Maven Plugin | ||
Insert a `<packages>` element into the GraalPy Maven Plugin `<configuration>` element, specifying the Python packages to download and install as shown below: | ||
|
||
|
||
resource:../../../pom.xml[tag=graalpy-maven-plugin] | ||
|
||
Adding `<package>pygal==3.0.4</package>` instructs the plugin to download and install the Python https://www.pygal.org/en/stable/[Pygal] package at build time. | ||
The package is included in the Java resources and becomes available to GraalPy. | ||
|
||
=== Python code | ||
We are going to use the https://www.pygal.org/en/stable/documentation/types/bar.html#stacked[StackedBar] class from https://www.pygal.org/en/stable/[Pygal] package to render a graph | ||
in https://www.pygal.org/en/3.0.0/documentation/output.html#svg[Svg] format. | ||
|
||
=== Java binding | ||
Python code can be accessed programmatically using the https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/package-summary.html[GraalVM SDK Polyglot API], | ||
which enables you to embed Python into your applications. | ||
|
||
In order to make it work, we first need a Java interface providing the intended binding to Python. | ||
|
||
Create a Java interface with the following code: | ||
source:PygalModule[] | ||
<1> The @api@/io/micronaut/graal/graalpy/annotations/GraalPyModule.html[@GraalPyModule] annotation indicates that the bean created from the interface | ||
is intended to import the `pygal` Python package and expose it to the Java code using the https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/Value.html#target-type-mapping-heading[Target type mapping]. | ||
<2> Java method matching the `StackedBar` Python class constructor. | ||
<3> Java interface with methods matching functions from the `StackedBar` Python class we want to use. | ||
<4> Java method matching the `StackedBar.add(title, values)` Python function. | ||
<5> Java method matching the `StackedBar.render()` Python function. | ||
<6> Java interface with methods matching functions from the `Svg` Python class we want to use. | ||
<7> Java method matching the `Svg.decode()` Python function. | ||
|
||
=== Controller | ||
To create a microservice that provides the graph rendered by `Pygal` you also need a controller. | ||
|
||
Create a controller: | ||
source:PygalController[] | ||
|
||
callout:controller[number=1,arg0=/pygal] | ||
callout:constructor-di[number=2,arg0=PygalModule] | ||
callout:get[number=3,arg0=index,arg1=/pygal] | ||
<5> By default, a Micronaut response uses `application/json` as Content-Type. We are returning `svg/xml`, so we have to set it. | ||
<6> Use the `PygalModule` bean to create a `PygalModule.StackedBar` instance. | ||
<7> Add graph title and data. | ||
<8> Render the graph. | ||
<9> Return the graphs `svg/xml` representation decoded as a string. | ||
|
||
=== Test | ||
|
||
Create a test to verify that when you make a GET request to `/pygal` you get the `svg/xml` response: | ||
|
||
test:PygalControllerTest[] | ||
|
||
callout:micronaut-test[1] | ||
callout:http-client[2] | ||
callout:http-request[3] | ||
|
||
common:testApp.adoc[] | ||
common:runapp.adoc[] | ||
|
||
Open `http://localhost:8080/pygal` in a web browser of your choice execute the endpoint. You should see a stacked bar chart. | ||
|
||
image::graalpy-pygal.png[] | ||
|
||
:exclude-for-languages:groovy | ||
|
||
== GraalVM Native Executable | ||
|
||
:leveloffset: +1 | ||
|
||
=== Native Executable metadata | ||
The https://www.graalvm.org/[GraalVM] Native Image compilation requires metadata to properly run code that uses https://www.graalvm.org/latest/reference-manual/native-image/metadata/#dynamic-proxy[dynamic proxies]. | ||
|
||
For the case that also a native executable has to be generated, create a proxy configuration file: | ||
resource:META-INF/native-image/proxy-config.json[] | ||
|
||
common:graal-with-plugins.adoc[] | ||
|
||
Open `http://localhost:8080/pygal` in a web browser of your choice to execute the endpoint exposed by the native executable. You should see a stacked bar chart. | ||
|
||
common:nativetest.adoc[] | ||
|
||
:exclude-for-languages: | ||
|
||
:leveloffset: -1 | ||
|
||
== Next steps | ||
|
||
Read more about https://micronaut-projects.github.io/micronaut-test/latest/guide/[Micronaut testing]. | ||
|
||
common:helpWithMicronaut.adoc[] |
37 changes: 37 additions & 0 deletions
37
guides/micronaut-graalpy/java/src/main/java/example/micronaut/HelloController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* | ||
* Copyright 2017-2024 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package example.micronaut; | ||
|
||
import io.micronaut.http.MediaType; | ||
import io.micronaut.http.annotation.Controller; | ||
import io.micronaut.http.annotation.Get; | ||
import io.micronaut.http.annotation.Produces; | ||
|
||
@Controller("/hello") // <1> | ||
class HelloController { | ||
|
||
private final HelloModule hello; | ||
|
||
HelloController(HelloModule hello) { // <2> | ||
this.hello = hello; | ||
} | ||
|
||
@Get // <3> | ||
@Produces(MediaType.TEXT_PLAIN) // <4> | ||
String index() { | ||
return hello.hello("World"); // <5> | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
guides/micronaut-graalpy/java/src/main/java/example/micronaut/HelloModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package example.micronaut; | ||
|
||
import io.micronaut.graal.graalpy.annotations.GraalPyModule; | ||
|
||
@GraalPyModule("hello") // <1> | ||
public interface HelloModule { | ||
String hello(String txt); // <2> | ||
} |
Oops, something went wrong.