diff --git a/.github/workflows/graalwasm-micronaut-excelize.yml b/.github/workflows/graalwasm-micronaut-excelize.yml new file mode 100644 index 00000000..de3eb604 --- /dev/null +++ b/.github/workflows/graalwasm-micronaut-excelize.yml @@ -0,0 +1,42 @@ +name: Test GraalWasm Micronaut Excelize Demo + +on: + push: + paths: + - 'graalwasm/graalwasm-micronaut-excelice/**' + - '.github/workflows/graalwasm-micronaut-excelice.yml' + pull_request: + paths: + - 'graalwasm/graalwasm-micronaut-excelice/**' + - '.github/workflows/graalwasm-micronaut-excelice.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + run: + name: 'graalwasm-micronaut-excelice' + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + - uses: graalvm/setup-graalvm@v1 + with: + java-version: '24.0.0' + distribution: 'graalvm' + github-token: ${{ secrets.GITHUB_TOKEN }} + cache: 'maven' + + - name: Package 'graalwasm-micronaut-excelice' + run: | + cd graalwasm/graalwasm-micronaut-excelice + ./mvnw --no-transfer-progress clean package + + - name: Run and Test Excelize App + run: | + cd graalwasm/graalwasm-micronaut-excelice + ./mvnw test & + sleep 10 + curl --fail-with-body --silent --dump-header - -o /dev/null http://localhost:8080 diff --git a/graalwasm/graalwasm-micronaut-excelize/.gitignore b/graalwasm/graalwasm-micronaut-excelize/.gitignore new file mode 100644 index 00000000..5a03bc30 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/.gitignore @@ -0,0 +1,15 @@ +Thumbs.db +.DS_Store +.gradle +build/ +target/ +out/ +.micronaut/ +.idea +*.iml +*.ipr +*.iws +.project +.settings +.classpath +.factorypath diff --git a/graalwasm/graalwasm-micronaut-excelize/.mvn/wrapper/maven-wrapper.properties b/graalwasm/graalwasm-micronaut-excelize/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..b252b8d0 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/graalwasm/graalwasm-micronaut-excelize/README.md b/graalwasm/graalwasm-micronaut-excelize/README.md new file mode 100644 index 00000000..5ac84590 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/README.md @@ -0,0 +1,39 @@ +# Excelize with GraalWasm Micronaut Demo + +This demo illustrates how GraalWasm can be used to embed [excelize-wasm](https://github.com/xuri/excelize-wasm/), a WebAssembly build of the Go [Excelize](https://github.com/xuri/excelize) library for reading and writing Microsoft Excel™ spreadsheets. +The demo also uses GraalJS to access the Excelize module through the WebAssembly JavaScript API. + +## Preparation + +Install GraalVM for JDK 24 and set the value of `JAVA_HOME` accordingly. +We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).) +```bash +sdk install java 24-graal +``` + +## Run the Application + +To start the demo, simply run: + +```bash +./mvnw package mn:run +``` + +When the demo runs, open the following URLs in a browser: + +- http://localhost:8080/ + + +This interface offers two core features: + +Import Excel to Database – Upload an Excel (.xlsx) file to parse and store its contents in the backend database. +Export Database to Excel – Retrieve data from the database and download it as a structured Excel (.xlsx) file. +Ideal for seamless data management via the browser. + + + + +## Implementation Details + +The [`Controller`](src/main/java/com/example/Controller.java) uses a [`Service`](src/main/java/com/example/ExcelizeService.java) That integrates the Excelize library with Micronaut and GraalVM to generate and read Excel files using WebAssembly (WASM) and JavaScript. It loads necessary JS and WASM files, executes JavaScript to manipulate Excel data, and reads the content back into a Java application. The results are saved as Excel files or processed into Java objects. + diff --git a/graalwasm/graalwasm-micronaut-excelize/aot-jar.properties b/graalwasm/graalwasm-micronaut-excelize/aot-jar.properties new file mode 100644 index 00000000..3820d55a --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/aot-jar.properties @@ -0,0 +1,37 @@ +# AOT configuration properties for jar packaging +# Please review carefully the optimizations enabled below +# Check https://micronaut-projects.github.io/micronaut-aot/latest/guide/ for more details + +# Caches environment property values: environment properties will be deemed immutable after application startup. +cached.environment.enabled=true + +# Precomputes Micronaut configuration property keys from the current environment variables +precompute.environment.properties.enabled=true + +# Replaces logback.xml with a pure Java configuration +logback.xml.to.java.enabled=true + +# Converts YAML configuration files to Java configuration +yaml.to.java.config.enabled=true + +# Scans for service types ahead-of-time, avoiding classpath scanning at startup +serviceloading.jit.enabled=true + +# Scans reactive types at build time instead of runtime +scan.reactive.types.enabled=true + +# Deduces the environment at build time instead of runtime +deduce.environment.enabled=true + +# Checks for the existence of some types at build time instead of runtime +known.missing.types.enabled=true + +# Precomputes property sources at build time +sealed.property.source.enabled=true + +# The list of service types to be scanned (comma separated) +service.types=io.micronaut.context.env.PropertySourceLoader,io.micronaut.inject.BeanConfiguration,io.micronaut.inject.BeanDefinitionReference,io.micronaut.http.HttpRequestFactory,io.micronaut.http.HttpResponseFactory,io.micronaut.core.beans.BeanIntrospectionReference,io.micronaut.core.convert.TypeConverterRegistrar,io.micronaut.context.env.PropertyExpressionResolver + +# A list of types that the AOT analyzer needs to check for existence (comma separated) +known.missing.types.list=io.reactivex.Observable,reactor.core.publisher.Flux,kotlinx.coroutines.flow.Flow,io.reactivex.rxjava3.core.Flowable,io.reactivex.rxjava3.core.Observable,io.reactivex.Single,reactor.core.publisher.Mono,io.reactivex.Maybe,io.reactivex.rxjava3.core.Single,io.reactivex.rxjava3.core.Maybe,io.reactivex.Completable,io.reactivex.rxjava3.core.Completable,io.methvin.watchservice.MacOSXListeningWatchService,io.micronaut.core.async.publisher.CompletableFuturePublisher,io.micronaut.core.async.publisher.Publishers.JustPublisher,io.micronaut.core.async.subscriber.Completable + diff --git a/graalwasm/graalwasm-micronaut-excelize/dependency-reduced-pom.xml b/graalwasm/graalwasm-micronaut-excelize/dependency-reduced-pom.xml new file mode 100644 index 00000000..4aef61b2 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/dependency-reduced-pom.xml @@ -0,0 +1,233 @@ + + + + micronaut-parent + io.micronaut.platform + 4.7.6 + ../pom.xml/pom.xml + + 4.0.0 + com.example + micronautdemo + ${packaging} + 0.1 + + + + maven-shade-plugin + 3.5.1 + + + package + + shade + + + + + org.openjdk.jmh.Main + + + + + + + + io.micronaut.maven + micronaut-maven-plugin + + aot-${packaging}.properties + + + + maven-enforcer-plugin + + + maven-compiler-plugin + + + + io.micronaut + micronaut-inject-java + ${micronaut.core.version} + + + org.openjdk.jmh + jmh-generator-annprocess + 1.37 + + + io.micronaut.data + micronaut-data-processor + ${micronaut.data.version} + + + io.micronaut + micronaut-inject + + + + + io.micronaut + micronaut-graal + ${micronaut.core.version} + + + io.micronaut + micronaut-http-validation + ${micronaut.core.version} + + + io.micronaut.serde + micronaut-serde-processor + ${micronaut.serialization.version} + + + io.micronaut + micronaut-inject + + + + + + -Amicronaut.processing.group=com.example + -Amicronaut.processing.module=micronautdemo + + + + + software.xdev + music-maven-plugin + 1.0.3 + + + validate + + music + + + + + + + https://incompetech.com/music/royalty-free/mp3-royaltyfree/Corncob.mp3 + + + true + + + + + + + central + https://repo.maven.apache.org/maven2 + + + + + io.micronaut + micronaut-http-client + 4.7.14 + test + + + micronaut-http-client-core + io.micronaut + + + micronaut-websocket + io.micronaut + + + netty-handler-proxy + io.netty + + + + + io.micronaut.test + micronaut-test-junit5 + 4.6.2 + test + + + micronaut-test-core + io.micronaut.test + + + + + org.junit.jupiter + junit-jupiter-api + 5.11.3 + test + + + opentest4j + org.opentest4j + + + junit-platform-commons + org.junit.platform + + + apiguardian-api + org.apiguardian + + + + + org.junit.jupiter + junit-jupiter-engine + 5.11.3 + test + + + junit-platform-engine + org.junit.platform + + + apiguardian-api + org.apiguardian + + + + + org.graalvm.polyglot + wasm + 24.2.1 + pom + compile + + + wasm + org.graalvm.wasm + + + + + org.graalvm.polyglot + js + 24.2.1 + pom + compile + + + js + org.graalvm.js + + + + + + false + com.example.Application + com.example.aot.generated + jar + netty + 4.7.6 + 21 + 21 + + diff --git a/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.23.02.161/graal_diagnostics_31839@5.zip b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.23.02.161/graal_diagnostics_31839@5.zip new file mode 100644 index 00000000..8915288e Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.23.02.161/graal_diagnostics_31839@5.zip differ diff --git a/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.23.02.161/graal_diagnostics_31839@5/wasm-function:7257/TruffleHotSpotCompilation-9712[wasm-function:7257].cfg b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.23.02.161/graal_diagnostics_31839@5/wasm-function:7257/TruffleHotSpotCompilation-9712[wasm-function:7257].cfg new file mode 100644 index 00000000..e05478cd --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.23.02.161/graal_diagnostics_31839@5/wasm-function:7257/TruffleHotSpotCompilation-9712[wasm-function:7257].cfg @@ -0,0 +1,5 @@ +begin_compilation + name " TruffleHotSpotCompilation-9712[wasm-function:7257]" + method "TruffleHotSpotCompilation-9712[wasm-function:7257]" + date 1750173782634 +end_compilation diff --git a/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.24.59.963/graal_diagnostics_31887@6.zip b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.24.59.963/graal_diagnostics_31887@6.zip new file mode 100644 index 00000000..43dc32c9 Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.24.59.963/graal_diagnostics_31887@6.zip differ diff --git a/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.29.29.074/graal_diagnostics_32030@6.zip b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.29.29.074/graal_diagnostics_32030@6.zip new file mode 100644 index 00000000..6e13cf9d Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.29.29.074/graal_diagnostics_32030@6.zip differ diff --git a/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.30.56.333/graal_diagnostics_32087@6.zip b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.30.56.333/graal_diagnostics_32087@6.zip new file mode 100644 index 00000000..a24d42a7 Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.30.56.333/graal_diagnostics_32087@6.zip differ diff --git a/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.34.17.787/graal_diagnostics_32147@6.zip b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.34.17.787/graal_diagnostics_32147@6.zip new file mode 100644 index 00000000..05eaf8b7 Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/graal_dumps/2025.06.17.16.34.17.787/graal_diagnostics_32147@6.zip differ diff --git a/graalwasm/graalwasm-micronaut-excelize/micronaut-cli.yml b/graalwasm/graalwasm-micronaut-excelize/micronaut-cli.yml new file mode 100644 index 00000000..dd29634b --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/micronaut-cli.yml @@ -0,0 +1,6 @@ +applicationType: default +defaultPackage: com.example +testFramework: junit +sourceLanguage: java +buildTool: maven +features: [app-name, http-client-test, java, java-application, junit, logback, maven, maven-enforcer-plugin, micronaut-aot, micronaut-http-validation, netty-server, properties, readme, serialization-jackson, shade, static-resources] diff --git a/graalwasm/graalwasm-micronaut-excelize/mvnw b/graalwasm/graalwasm-micronaut-excelize/mvnw new file mode 100755 index 00000000..88228874 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/mvnw @@ -0,0 +1,287 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.1.1 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + printf '%s' "$(cd "$basedir"; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname $0)") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $wrapperUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + QUIET="--quiet" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + elif command -v curl > /dev/null; then + QUIET="--silent" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + QUIET="" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L + fi + [ $? -eq 0 ] || rm -f "$wrapperJarPath" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=`cygpath --path --windows "$javaSource"` + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/graalwasm/graalwasm-micronaut-excelize/mvnw.bat b/graalwasm/graalwasm-micronaut-excelize/mvnw.bat new file mode 100644 index 00000000..1d7c59be --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/mvnw.bat @@ -0,0 +1,187 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.1.1 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/graalwasm/graalwasm-micronaut-excelize/output.xlsx b/graalwasm/graalwasm-micronaut-excelize/output.xlsx new file mode 100644 index 00000000..a4361a26 Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/output.xlsx differ diff --git a/graalwasm/graalwasm-micronaut-excelize/pom.xml b/graalwasm/graalwasm-micronaut-excelize/pom.xml new file mode 100644 index 00000000..349a11e0 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/pom.xml @@ -0,0 +1,219 @@ + + + 4.0.0 + com.example + micronautdemo + 0.1 + ${packaging} + + + io.micronaut.platform + micronaut-parent + 4.7.6 + + + jar + 4.7.6 + false + com.example.aot.generated + netty + com.example.Application + 25 + 25 + UTF-8 + 25.0.0-SNAPSHOT + + + + + central + https://repo.maven.apache.org/maven2 + + + + + + org.graalvm.polyglot + polyglot + ${graal.languages.version} + + + org.graalvm.polyglot + js + ${graal.languages.version} + pom + + + org.graalvm.polyglot + wasm + ${graal.languages.version} + pom + + + io.micronaut + micronaut-http-server-netty + compile + + + io.micronaut.serde + micronaut-serde-jackson + compile + + + io.micronaut.views + micronaut-views-thymeleaf + + + io.micronaut.data + micronaut-data-hibernate-jpa + + + + + io.micronaut.sql + micronaut-jdbc-hikari + + + + + com.h2database + h2 + runtime + + + + + io.micronaut.data + micronaut-data-processor + + + + ch.qos.logback + logback-classic + runtime + + + io.micronaut + micronaut-http-client + test + + + io.micronaut.test + micronaut-test-junit5 + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.openjdk.jmh + jmh-core + 1.37 + + + org.openjdk.jmh + jmh-generator-annprocess + 1.37 + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + package + shade + + + + org.openjdk.jmh.Main + + + + + + + + io.micronaut.maven + micronaut-maven-plugin + + aot-${packaging}.properties + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + + io.micronaut + micronaut-inject-java + ${micronaut.core.version} + + + org.openjdk.jmh + jmh-generator-annprocess + 1.37 + + + + io.micronaut.data + micronaut-data-processor + ${micronaut.data.version} + + + io.micronaut + micronaut-inject + + + + + io.micronaut + micronaut-graal + ${micronaut.core.version} + + + io.micronaut + micronaut-http-validation + ${micronaut.core.version} + + + io.micronaut.serde + micronaut-serde-processor + ${micronaut.serialization.version} + + + io.micronaut + micronaut-inject + + + + + + -Amicronaut.processing.group=com.example + -Amicronaut.processing.module=micronautdemo + + + + + + + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Application.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Application.java new file mode 100644 index 00000000..e7d686b2 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Application.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.org/license/UPL. + */ + +package com.example; + +import io.micronaut.runtime.Micronaut; + + +public class Application { + public static void main(String[] args) { + Micronaut.run(Application.class, args); + } +} + + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/BenchmarkRunner.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/BenchmarkRunner.java new file mode 100644 index 00000000..1cccba4e --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/BenchmarkRunner.java @@ -0,0 +1,56 @@ +package com.example; + +import io.micronaut.core.io.ResourceResolver; +import org.openjdk.jmh.annotations.*; + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +@Warmup(iterations = 2, time = 5) +@Measurement(iterations = 3, time = 5) +@Fork(1) + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +public class BenchmarkRunner { + + ExcelizeService excelizeService; + List sampleBooks; + byte[] excelData; + + @Setup(Level.Trial) + public void setup() throws IOException { + + ResourceResolver resolver = new ResourceResolver(); + ExcelizePool excelizePool= new ExcelizePool(resolver); + this.excelizeService = new ExcelizeService(excelizePool); + + + // Generate fake book list + sampleBooks = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + sampleBooks.add(new Book("Book " + i, "Author " + i)); + } + + // Generate Excel data once for readExcelFromFile + excelData = Files.readAllBytes(Paths.get("src/main/resources/output.xlsx")); + } + + @Benchmark + public void benchmarkExcelExport() throws IOException { + byte[] data = excelizeService.runExcelizeComplete(sampleBooks); + //System.out.println(data.length); + } + + @Benchmark + public void benchmarkExcelImport() throws IOException { + List books = excelizeService.readExcelFromFile(excelData); + //System.out.println(books); + } +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Book.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Book.java new file mode 100644 index 00000000..bb5689ef --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Book.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.org/license/UPL. + */ + +package com.example; + + + + +import io.micronaut.serde.annotation.Serdeable; +import jakarta.persistence.*; + + +@Entity +@Serdeable + +public class Book { + + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "title") + private String title; + @Column(name = "author") + private String author; + + + public Book() { + + } + public Book(String id,String title, String author) { + + this.id= Long.valueOf(id); + this.title = title; + this.author = author; + } + public Book(String title, String author) { + + this.title = title; + this.author = author; + } + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public String getAuthor() { + return author; + } + + public void setId(Long id) { + this.id = id; + } + +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Controller.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Controller.java new file mode 100644 index 00000000..9d3b4d28 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Controller.java @@ -0,0 +1,79 @@ +package com.example; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Part; +import io.micronaut.http.annotation.Post; +import io.micronaut.http.multipart.CompletedFileUpload; +import jakarta.inject.Inject; +import io.micronaut.views.View; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +@io.micronaut.http.annotation.Controller +public class Controller { + + + private final Repository repository; + private final ExcelizeService service; + + public Controller(ExcelizeService service ,Repository repository){ + repository.save(new Book("Dragons","Anwar")); + repository.save(new Book("Time","Anwar2")); + repository.save(new Book("dragon lord","Anwar3")); + this.service = service; + this.repository = repository; + } + + + @Get + @View("index") + public void index() { + + } + + @Get(value="download", produces = MediaType.APPLICATION_OCTET_STREAM) + public HttpResponse downloadExcel() throws IOException { + + List books= repository.findAll(); + byte[] fileContent = service.runExcelizeComplete(books); + return HttpResponse.ok(fileContent) + .header("Content-Disposition", "attachment; filename=output.xlsx") + .contentType(MediaType.APPLICATION_OCTET_STREAM); + } + + + + @Post(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA) + public HttpResponse uploadExcelFile(@Part("file") CompletedFileUpload file) { + String filename = file.getFilename(); + + if (!filename.endsWith(".xlsx")) { + return HttpResponse.badRequest("Invalid file type. Only .xlsx files are accepted."); + } + + try { + byte[] fileBytes = file.getBytes(); + List books = service.readExcelFromFile(fileBytes); + for(Book book : books){ + if (book.getId() == null || + repository.findById(book.getId()).isEmpty()) { + book.setId(null); + repository.save(book); + } else { + repository.update(book); + } + } + + return HttpResponse.ok("the file was saved in the database successfully :" + filename); + } catch (IOException e) { + return HttpResponse.serverError("Error reading file: " + e.getMessage()); + } + } +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/ExcelizePool.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/ExcelizePool.java new file mode 100644 index 00000000..d68e0840 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/ExcelizePool.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.org/license/UPL. + */ + +package com.example; + +import io.micronaut.core.io.ResourceResolver; +import org.graalvm.polyglot.*; +import org.graalvm.polyglot.io.IOAccess; + +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +@io.micronaut.context.annotation.Context +public class ExcelizePool { + private final BlockingQueue contexts; + + + public ExcelizePool(ResourceResolver resourceResolver) throws IOException { + + // Read the WASM file bytes + byte[] excelizeWasmBytes = resourceResolver.getResourceAsStream("classpath:excelize.wasm").get().readAllBytes(); + + + System.out.println("Executing excelize read..."); + + String test = new String(resourceResolver.getResourceAsStream("classpath:excelize.js").get().readAllBytes(), StandardCharsets.UTF_8); + String prep = new String(resourceResolver.getResourceAsStream("classpath:excelize_prep.js").get().readAllBytes(), StandardCharsets.UTF_8); + String excelizeLib = new String(resourceResolver.getResourceAsStream("classpath:excelize_m.js").get().readAllBytes(), StandardCharsets.UTF_8); + + // Configure options (same as your write code) + Map options = new HashMap<>(); + //options.put("engine.CompilationFailureAction", "Diagnose"); + options.put("js.top-level-await", "true"); + options.put("js.webassembly", "true"); + options.put("js.commonjs-require", "true"); + options.put("js.text-encoding","true"); + + Map engineOptions = new HashMap<>(); + engineOptions.put("engine.CompilerThreads", "1"); + engineOptions.put("engine.WarnInterpreterOnly", "false"); + engineOptions.put("engine.CompilationFailureAction", "Diagnose"); + engineOptions.put("engine.MultiTier", "true"); + engineOptions.put("engine.Mode", "throughput"); + int maxThreads = Runtime.getRuntime().availableProcessors(); + contexts = new LinkedBlockingQueue<>(maxThreads); + Engine engine = Engine.newBuilder("js", "wasm") + .allowExperimentalOptions(true) + .options(engineOptions) + .build(); + for (int i = 0; i < maxThreads; i++) { + Context context = Context.newBuilder("js", "wasm") + .engine(engine) + .allowAllAccess(true) + .options(options) + .build(); + + Source prepModule = Source.newBuilder("js", prep, "prep.js").build(); + context.eval(prepModule); + + // Load the excelize module as an ECMAScript module + Source excelizeModule = Source.newBuilder("js", excelizeLib, "excelize.mjs") + .mimeType("application/javascript+module") + .build(); + Value excelizeMod = context.eval(excelizeModule); + context.getPolyglotBindings().putMember("excelize", excelizeMod); + context.getBindings("js").putMember("wasmBytes", excelizeWasmBytes); + Source testRun = Source.newBuilder("js", test, "excelize.js").build(); + context.eval(testRun); + + this.contexts.add(context); + } + } + + + + public Context getContext() { + try { + return contexts.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + void release(Context context) { + contexts.add(context); + } +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/ExcelizeService.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/ExcelizeService.java new file mode 100644 index 00000000..f95a2230 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/ExcelizeService.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. + * + * Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.org/license/UPL. + */ + +package com.example; + +import org.graalvm.polyglot.*; +import org.graalvm.polyglot.proxy.ProxyExecutable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + + + +@io.micronaut.context.annotation.Context +public class ExcelizeService { + + + private final ExcelizePool excelizePool; + + public ExcelizeService(ExcelizePool excelizePool) { + this.excelizePool = excelizePool; + } + public byte[] runExcelizeComplete(List books) throws IOException { + Context context = excelizePool.getContext(); + try{ + Value function = context.getBindings("js").getMember("generateExcel"); + Value jsArray = context.eval("js", "[]"); + + // Add headers as the first row + Value headerRow = context.eval("js", "[]"); + headerRow.setArrayElement(0, "ID"); + headerRow.setArrayElement(1, "Title"); + headerRow.setArrayElement(2, "Author"); + jsArray.setArrayElement(0, headerRow); + + // Add book data rows + for (int i = 0; i < books.size(); i++) { + Book b = books.get(i); + Value jsRow = context.eval("js", "[]"); + jsRow.setArrayElement(0, b.getId() == null ? "" : b.getId()); + jsRow.setArrayElement(1, b.getTitle()); + jsRow.setArrayElement(2, b.getAuthor()); + jsArray.setArrayElement(i + 1, jsRow); + } + + + function.execute(jsArray); + // Save output Excel file + Value buffer = context.getPolyglotBindings().getMember("excelBuffer"); + if (buffer != null && buffer.hasArrayElements()) { + int length = (int) buffer.getArraySize(); + byte[] fileBytes = new byte[length]; + for (int j = 0; j < length; j++) { + fileBytes[j] = (byte) buffer.getArrayElement(j).asInt(); + } + return fileBytes; + } else { + System.err.println("No buffer exported from JS."); + } + }finally { + excelizePool.release(context); + } + return null; + } + + public List readExcelFromFile(byte[] excelBytes) throws IOException { + + Context context = excelizePool.getContext(); + List books = new ArrayList<>(); + try { + + Value readFunc = context.getBindings("js").getMember("readExcel"); + readFunc.execute(excelBytes).invokeMember("then",(ProxyExecutable) result -> { + Value bufferArray = result[0]; + if (bufferArray.hasArrayElements()) { + for (int i = 1; i < bufferArray.getArraySize(); i++) { + Value row = bufferArray.getArrayElement(i); + if (row.hasArrayElements()) { + + String id = row.getArrayElement(0).asString(); + String author = row.getArrayElement(1).asString(); + String title = row.getArrayElement(2).asString(); + // Create a new Book object and add it to the list + books.add(new Book(id, author, title)); + } + } + } + return null; + + }); + + }finally { + excelizePool.release(context); + } + return books; + } + } + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/MainBenchmark.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/MainBenchmark.java new file mode 100644 index 00000000..4d48315b --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/MainBenchmark.java @@ -0,0 +1,7 @@ +package com.example; + +public class MainBenchmark { + public static void main(String[] args) throws Exception { + org.openjdk.jmh.Main.main(args); + } +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Repository.java b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Repository.java new file mode 100644 index 00000000..0d0dfd45 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/java/com/example/Repository.java @@ -0,0 +1,10 @@ +package com.example; + +import io.micronaut.data.jpa.repository.JpaRepository; +import io.micronaut.data.repository.CrudRepository; +import io.micronaut.transaction.annotation.Transactional; + +@io.micronaut.data.annotation.Repository +@Transactional +public interface Repository extends CrudRepository { +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/.gitignore b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/application.properties b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/application.properties new file mode 100644 index 00000000..fcbe56d0 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/application.properties @@ -0,0 +1,19 @@ + +micronaut.application.name=micronautdemo + + +datasources.default.url=jdbc:h2:mem:devDb +datasources.default.driverClassName=org.h2.Driver +datasources.default.username=sa +datasources.default.password= +datasources.default.dialect=H2 +datasources.default.schema-generate=CREATE_DROP + +jpa.default.properties.hibernate.hbm2ddl.auto=update +jpa.default.properties.hibernate.show_sql=true + + +micronaut.views.enabled=true +micronaut.views.defaultRenderer=thymeleaf + + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize.js b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize.js new file mode 100644 index 00000000..cb6ab7db --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize.js @@ -0,0 +1,70 @@ +let f; +let reader = null; +global.excelize = {}; +const go = new Go(); +WebAssembly.instantiate(new Uint8Array(wasmBytes), go.importObject).then((result) => {go.run(result.instance);}); + +function generateExcel(data) { + new Promise((resolve, reject) => { + f = excelize.NewFile(); + data.forEach((row, idx) => { + const ret1 = excelize.CoordinatesToCellName(1, idx + 1); + if (ret1.error) { + console.log(ret1.error); + reject(ret1.error); + return; + } + const res2 = f.SetSheetRow('Sheet1', ret1.cell, row); + if (res2.error) { + console.log(res2.error); + reject(res2.error); + return; + } + }); + + + const { buffer, error } = f.WriteToBuffer(); + if (error) { + console.error(error); + reject(error); + } else { + // Expose the buffer to Java + Polyglot.export("excelBuffer", buffer); + resolve(buffer); + } + }) + +} + +function readExcel(excelFileBytes) { + let result ; + + return new Promise((resolve, reject) => { + + + if(reader == null) reader = excelize.OpenReader(new Uint8Array(excelFileBytes)); + + + + // Get all rows from Sheet1 + const ret2 = reader.GetRows('Sheet1'); + if (ret2.error) { + console.error(ret2.error); + reject(ret2.error); // Reject promise in case of error + } else { + // Format the rows into a simple array format for return + const resultArray = ret2.result.map(row => row.map(colCell => colCell)); + //console.log("Extracted data:", resultArray); + + + // Resolve the promise with the result array + + Polyglot.export("resultArray", resultArray); + resolve(resultArray); + } + + //console.log("Excel read successfully."); + + }); +} + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize.wasm b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize.wasm new file mode 100755 index 00000000..2837a0fa Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize.wasm differ diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize_m.js b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize_m.js new file mode 100644 index 00000000..f4227be6 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize_m.js @@ -0,0 +1,566 @@ +//this file comes from Dmitrii Nikeshkin +//Github : https://github.com/DimaNike + +'use strict'; + +if (typeof window === 'undefined') { + global.crypto = { + getRandomValues(b) { + return nodeCrypto.randomFillSync(b); + } + }; + global.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + } + }; +} +(() => { + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!globalThis.fs) { + let outputBuf = ""; + globalThis.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substr(0, nl)); + outputBuf = outputBuf.substr(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!globalThis.process) { + globalThis.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!globalThis.crypto) { + throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"); + } + + if (!globalThis.performance) { + throw new Error("globalThis.performance is not available, polyfill required (performance.now only)"); + } + + if (!globalThis.TextEncoder) { + throw new Error("globalThis.TextEncoder is not available, polyfill required"); + } + + if (!globalThis.TextDecoder) { + throw new Error("globalThis.TextDecoder is not available, polyfill required"); + } + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + globalThis.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + gojs: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + sp >>>= 0; + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + sp >>>= 0; + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + sp >>>= 0; + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error("Go.run: WebAssembly.Instance expected"); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + globalThis, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [globalThis, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192; + if (offset >= wasmMinDataAddr) { + throw new Error("total length of command line and environment variables exceeds limit"); + } + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } +})(); \ No newline at end of file diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize_prep.js b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize_prep.js new file mode 100644 index 00000000..81803f06 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/excelize_prep.js @@ -0,0 +1,42 @@ +//this file comes from Dmitrii Nikeshkin +//Github : https://github.com/DimaNike + + +(async () => { + +globalThis.setTimeout = (fn, ms) => { + // Use GraalVM's Java `Thread.sleep` if available + const Thread = Java.type("java.lang.Thread"); + Thread.sleep(ms); + fn(); // run after sleep + return 0; // fake timeout ID + }; + globalThis.clearTimeout = function (id) {}; + + global = globalThis; + const nowOffset = Date.now(); + const now = () => Date.now() - nowOffset; + global.process = {}; + global.nodeCrypto = {}; + global.process.hrtime = global.process.hrtime || ((previousTimestamp) => { + const baseNow = Math.floor((Date.now() - now()) * 1e-3) + const clocktime = now() * 1e-3 + let seconds = Math.floor(clocktime) + baseNow + let nanoseconds = Math.floor((clocktime % 1) * 1e9) + + if (previousTimestamp) { + seconds = seconds - previousTimestamp[0] + nanoseconds = nanoseconds - previousTimestamp[1] + if (nanoseconds < 0) { + seconds-- + nanoseconds += 1e9 + } + } + return [seconds, nanoseconds] + }); + global.nodeCrypto.randomFillSync = function(number) { + return 123; + }; +})(); + + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/logback.xml b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/logback.xml new file mode 100644 index 00000000..2d77bdab --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/output.xlsx b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/output.xlsx new file mode 100644 index 00000000..a4361a26 Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/output.xlsx differ diff --git a/graalwasm/graalwasm-micronaut-excelize/src/main/resources/views/index.html b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/views/index.html new file mode 100644 index 00000000..f46f57d0 --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/main/resources/views/index.html @@ -0,0 +1,151 @@ + + + + + + + + Excel File Upload and Download + + + + +
+

Upload an Excel File

+
+ + +
+
+ +
+ +
+

Download Excel File

+ +
+ + + + diff --git a/graalwasm/graalwasm-micronaut-excelize/src/test/java/com/example/MicronautdemoTest.java b/graalwasm/graalwasm-micronaut-excelize/src/test/java/com/example/MicronautdemoTest.java new file mode 100644 index 00000000..7185110e --- /dev/null +++ b/graalwasm/graalwasm-micronaut-excelize/src/test/java/com/example/MicronautdemoTest.java @@ -0,0 +1,49 @@ +package com.example; + +import io.micronaut.http.HttpRequest; +import io.micronaut.http.MediaType; +import io.micronaut.http.client.HttpClient; +import io.micronaut.http.client.annotation.Client; +import io.micronaut.http.client.multipart.MultipartBody; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.*; + +@MicronautTest +public class MicronautdemoTest { + + @Inject + @Client("/") + HttpClient client; + + @Test + void testDownloadExcelEndpoint() { + byte[] response = client.toBlocking().retrieve(HttpRequest.GET("/download"), byte[].class); + + assertNotNull(response); + assertTrue(response.length > 0, "Excel file should not be empty"); + } + + @Test + void testUploadExcelEndpoint() throws IOException { + InputStream is = getClass().getResourceAsStream("/test.xlsx"); + assertNotNull(is, "output.xlsx should exist in src/test/resources"); + + MultipartBody body = MultipartBody.builder() + .addPart("file", "output.xlsx", MediaType.APPLICATION_OCTET_STREAM_TYPE, is.readAllBytes()) + .build(); + + String response = client.toBlocking().retrieve(HttpRequest.POST("/upload", body) + .contentType(MediaType.MULTIPART_FORM_DATA_TYPE), String.class); + + assertTrue(response.contains("successfully"), "Upload should respond with success message"); + } +} diff --git a/graalwasm/graalwasm-micronaut-excelize/src/test/resources/test.xlsx b/graalwasm/graalwasm-micronaut-excelize/src/test/resources/test.xlsx new file mode 100644 index 00000000..a4361a26 Binary files /dev/null and b/graalwasm/graalwasm-micronaut-excelize/src/test/resources/test.xlsx differ