Skip to content

Commit

Permalink
Split Native Runtime Hints generation in appropriate packages + try t…
Browse files Browse the repository at this point in the history
…o register types using the mappings.xml
  • Loading branch information
filiphr authored and jbarrez committed Feb 7, 2024
1 parent 5a31cf8 commit 92c5d4d
Show file tree
Hide file tree
Showing 47 changed files with 1,312 additions and 1,062 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/graal-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Flowable Graal Build

on:
push:
branches:
- main
- 'flowable-release-*'

env:
MAVEN_ARGS: >-
-B -V --no-transfer-progress
-Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
test_graal:
name: Linux Graal Native
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
java-version: 17
distribution: graalvm
- name: Cache Maven Repository
uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Install
run: ./mvnw install -Pdistro ${MAVEN_ARGS} -DskipTests=true -Dmaven.javadoc.skip=true
- name: Test
run: ./mvnw test -PnativeTest,native,distro,errorLogging ${MAVEN_ARGS} -Dmaven.test.redirectTestOutputToFile=false -pl modules/flowable-spring-boot/flowable-spring-boot-samples/flowable-spring-boot-sample-native
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* 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
*
* http://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 org.flowable.app.engine.impl.aot;

import org.flowable.common.engine.impl.aot.FlowableMyBatisResourceHintsRegistrar;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

/**
* @author Filip Hrisafov
*/
public class FlowableAppRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ResourceHints resourceHints = hints.resources();
resourceHints.registerPattern("org/flowable/app/db/liquibase/flowable-app-db-changelog.xml");
FlowableMyBatisResourceHintsRegistrar.registerMappingResources("org/flowable/app/db/mapping", hints, classLoader);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.flowable.app.engine.impl.aot.FlowableAppRuntimeHints
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* 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
*
* http://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 org.flowable.batch.service.impl.aot;

import org.flowable.common.engine.impl.aot.FlowableSqlResourceHintsRegistrar;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

/**
* @author Filip Hrisafov
*/
public class FlowableBatchServiceRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ResourceHints resourceHints = hints.resources();
FlowableSqlResourceHintsRegistrar.registerSqlResources("org/flowable/batch/service/db", resourceHints);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.flowable.batch.service.impl.aot.FlowableBatchServiceRuntimeHints
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* 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
*
* http://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 org.flowable.cmmn.engine.impl.aot;

import org.flowable.common.engine.impl.aot.FlowableMyBatisResourceHintsRegistrar;
import org.flowable.variable.service.impl.QueryVariableValue;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

/**
* @author Filip Hrisafov
*/
public class FlowableCmmnRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ResourceHints resourceHints = hints.resources();
FlowableMyBatisResourceHintsRegistrar.registerMappingResources("org/flowable/cmmn/db/mapping", hints, classLoader);
resourceHints.registerPattern("org/flowable/cmmn/db/liquibase/flowable-cmmn-db-changelog.xml");
resourceHints.registerPattern("org/flowable/cmmn/db/liquibase/flowable-cmmn-db-changelog-crdb.xml");
resourceHints.registerPattern("org/flowable/impl/cmmn/parser/*.xsd");

hints.reflection()
.registerType(QueryVariableValue.class, MemberCategory.INVOKE_PUBLIC_METHODS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.flowable.cmmn.engine.impl.aot.FlowableCmmnRuntimeHints
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* 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
*
* http://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 org.flowable.dmn.engine.impl.aot;

import org.flowable.common.engine.impl.aot.FlowableMyBatisResourceHintsRegistrar;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

/**
* @author Filip Hrisafov
*/
public class FlowableDmnRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ResourceHints resourceHints = hints.resources();
FlowableMyBatisResourceHintsRegistrar.registerMappingResources("org/flowable/dmn/db/mapping", hints, classLoader);
resourceHints.registerPattern("org/flowable/dmn/db/liquibase/flowable-dmn-db-changelog.xml");
resourceHints.registerPattern("org/flowable/impl/dmn/parser/*.xsd");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.flowable.dmn.engine.impl.aot.FlowableDmnRuntimeHints
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* 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
*
* http://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 org.flowable.common.engine.impl.aot;

import org.flowable.common.engine.impl.persistence.entity.ByteArrayRefTypeHandler;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

/**
* @author Filip Hrisafov
*/
public class FlowableCommonRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ResourceHints resourceHints = hints.resources();
resourceHints.registerPattern("META-INF/services/liquibase.hub.HubService");
resourceHints.registerPattern("META-INF/services/liquibase.license.LicenseService");
resourceHints.registerResourceBundle("org.flowable.common.engine.impl.de.odysseus.el.misc.LocalStrings");
// If we can detect which DB is being used we can perhaps register only the appropriate DB file
resourceHints.registerPattern("org/flowable/common/db/properties/*.properties");
FlowableSqlResourceHintsRegistrar.registerSqlResources("org/flowable/common/db", resourceHints);

hints.reflection()
.registerType(ByteArrayRefTypeHandler.class, MemberCategory.values());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/* 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
*
* http://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 org.flowable.common.engine.impl.aot;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeReference;
import org.springframework.core.io.ClassPathResource;

/**
* Register the necessary resource hints for the Flowable SQL resources.
*
* @author Filip Hrisafov
*/
public class FlowableMyBatisResourceHintsRegistrar {

public static void registerMappingResources(String baseFolder, RuntimeHints runtimeHints, ClassLoader classLoader) {
ResourceHints resourceHints = runtimeHints.resources();
String mappingsPath = baseFolder + "/mappings.xml";
ClassPathResource mappingsResource = new ClassPathResource(mappingsPath);
resourceHints.registerResource(mappingsResource);
try (InputStream mappingsStream = mappingsResource.getInputStream()) {
XPathParser parser = createParser(mappingsStream);

List<XNode> mappers = parser.evalNodes("/configuration/mappers/mapper");
for (XNode mapper : mappers) {
registerMapper(mapper.getStringAttribute("resource"), runtimeHints, classLoader);
}
} catch (IOException e) {
throw new UncheckedIOException("Failed to read mappings " + mappingsPath, e);
}
}

public static void registerMapper(String mapperPath, RuntimeHints hints, ClassLoader classLoader) {
ResourceHints resourceHints = hints.resources();
ClassPathResource mapperResource = new ClassPathResource(mapperPath);
resourceHints.registerResource(mapperResource);

ReflectionHints reflectionHints = hints.reflection();
MemberCategory[] memberCategories = MemberCategory.values();
try (InputStream mapperStream = mapperResource.getInputStream()) {
XPathParser parser = createParser(mapperStream);
XNode mapper = parser.evalNode("/mapper");
// The xpath resolving is similar like what MyBatis does in XMLMapperBuilder#parse
for (XNode resultMap : mapper.evalNodes("/mapper/resultMap")) {
String type = resultMap.getStringAttribute("type");
if (type != null) {
reflectionHints.registerType(TypeReference.of(type), memberCategories);
}
}

for (XNode statement : mapper.evalNodes("select|insert|update|delete")) {
String parameterType = statement.getStringAttribute("parameterType");
if (parameterType != null) {
if (parameterType.startsWith("org.flowable") || parameterType.startsWith("java.")) {
reflectionHints.registerType(TypeReference.of(parameterType), memberCategories);
} else if (parameterType.equals("map")) {
reflectionHints.registerType(Map.class, memberCategories);
}
}

String resultType = statement.getStringAttribute("resultType");
if (resultType != null) {
if (resultType.equals("long")) {
reflectionHints.registerType(long.class, memberCategories);
reflectionHints.registerType(Long.class, memberCategories);
} else if (resultType.equals("string")) {
reflectionHints.registerType(String.class, memberCategories);
} else if (resultType.equals("map")) {
reflectionHints.registerType(HashMap.class, memberCategories);
}
}
}

} catch (IOException e) {
throw new UncheckedIOException("Failed to read mapper from " + mapperPath, e);
}
}

protected static XPathParser createParser(InputStream stream) {
return new XPathParser(stream, false, null, new XMLMapperEntityResolver());
}

}
Loading

0 comments on commit 92c5d4d

Please sign in to comment.