You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2023/06/16 16:18:01 UTC
[camel-spring-boot] 01/01: CAMEL-19308: Add minimal support of AOT mode
This is an automated email from the ASF dual-hosted git repository.
nfilotto pushed a commit to branch CAMEL-19308/add-minimal-aot-support
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git
commit ea14baab3cd414a5623579320e2eb8d6bf10e02e
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Fri Jun 16 18:13:18 2023 +0200
CAMEL-19308: Add minimal support of AOT mode
---
.../camel/springboot/catalog/others.properties | 1 +
.../camel/springboot/catalog/others/xml-jaxb.json | 15 +
components-starter/camel-xml-jaxb-starter/pom.xml | 66 +++++
.../xml/jaxb/springboot/JAXBRuntimeHints.java | 315 +++++++++++++++++++++
.../jaxb/springboot/graalvm/JAXBSubstitutions.java | 101 +++++++
.../src/main/resources/META-INF/LICENSE.txt | 203 +++++++++++++
.../src/main/resources/META-INF/NOTICE.txt | 11 +
.../src/main/resources/META-INF/spring.provides | 17 ++
.../main/resources/META-INF/spring/aot.factories | 2 +
.../camel/xml/jaxb/springboot/BeanScope.java | 25 +-
.../org/apache/camel/xml/jaxb/springboot/Book.java | 38 ++-
.../camel/xml/jaxb/springboot/IdentifiedType.java | 39 ++-
.../xml/jaxb/springboot/JAXBRuntimeHintsTest.java | 53 ++++
.../camel/xml/jaxb/springboot/USAddress.java | 201 +++++++++++++
.../apache/camel/xml/jaxb/springboot/jaxb.index | 1 +
components-starter/pom.xml | 1 +
.../camel/spring/boot/CamelAutoConfiguration.java | 8 +-
.../boot/CamelSpringBootApplicationController.java | 4 +-
.../camel/spring/boot/SpringPropertiesParser.java | 7 +-
.../camel/spring/boot/aot/CamelRuntimeHints.java | 98 +++++++
.../camel/spring/boot/aot/ReflectionHelper.java | 238 ++++++++++++++++
.../camel/spring/boot/aot/RuntimeHintsHelper.java | 71 +++++
.../camel-spring-boot/native-image.properties | 1 +
.../spring/boot/aot/CamelRuntimeHintsTest.java | 52 ++++
.../spring/boot/aot/ReflectionHelperTest.java | 142 ++++++++++
dsl-starter/camel-xml-jaxb-dsl-starter/pom.xml | 5 +
.../jaxb/springboot/aot/XMLDSLRuntimeHints.java | 24 +-
.../main/resources/META-INF/spring/aot.factories | 2 +
pom.xml | 2 +
29 files changed, 1675 insertions(+), 68 deletions(-)
diff --git a/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others.properties b/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others.properties
index 7af10075c1a..240ffc8defe 100644
--- a/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others.properties
+++ b/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others.properties
@@ -29,6 +29,7 @@ shiro
spring-security
undertow-spring-security
xml-io-dsl
+xml-jaxb
xml-jaxb-dsl
xml-jaxp
yaml-dsl
diff --git a/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others/xml-jaxb.json b/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others/xml-jaxb.json
new file mode 100644
index 00000000000..fb3fe104749
--- /dev/null
+++ b/catalog/camel-catalog-provider-springboot/src/main/resources/org/apache/camel/springboot/catalog/others/xml-jaxb.json
@@ -0,0 +1,15 @@
+{
+ "other": {
+ "kind": "other",
+ "name": "xml-jaxb",
+ "title": "Xml Jaxb",
+ "description": "Camel XML JAXB",
+ "deprecated": false,
+ "firstVersion": "3.1.0",
+ "label": "dsl",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel.springboot",
+ "artifactId": "camel-xml-jaxb-starter",
+ "version": "4.0.0-SNAPSHOT"
+ }
+}
diff --git a/components-starter/camel-xml-jaxb-starter/pom.xml b/components-starter/camel-xml-jaxb-starter/pom.xml
new file mode 100644
index 00000000000..be459406cdb
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>components-starter</artifactId>
+ <version>4.0.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>camel-xml-jaxb-starter</artifactId>
+ <packaging>jar</packaging>
+ <description>Spring-Boot Starter for Camel JAXB Support</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter</artifactId>
+ <version>${spring-boot-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-xml-jaxb</artifactId>
+ <version>${camel-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.graalvm.sdk</groupId>
+ <artifactId>graal-sdk</artifactId>
+ <version>${graal-sdk-version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-test</artifactId>
+ <version>${spring-boot-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core-test</artifactId>
+ <version>${spring-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <!--START OF GENERATED CODE-->
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-core-starter</artifactId>
+ </dependency>
+ <!--END OF GENERATED CODE-->
+ </dependencies>
+</project>
diff --git a/components-starter/camel-xml-jaxb-starter/src/main/java/org/apache/camel/xml/jaxb/springboot/JAXBRuntimeHints.java b/components-starter/camel-xml-jaxb-starter/src/main/java/org/apache/camel/xml/jaxb/springboot/JAXBRuntimeHints.java
new file mode 100644
index 00000000000..d8002c20ecf
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/main/java/org/apache/camel/xml/jaxb/springboot/JAXBRuntimeHints.java
@@ -0,0 +1,315 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.xml.jaxb.springboot;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.xml.bind.annotation.XmlAccessOrder;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAnyAttribute;
+import jakarta.xml.bind.annotation.XmlAnyElement;
+import jakarta.xml.bind.annotation.XmlAttachmentRef;
+import jakarta.xml.bind.annotation.XmlAttribute;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlElementDecl;
+import jakarta.xml.bind.annotation.XmlElementRef;
+import jakarta.xml.bind.annotation.XmlElementRefs;
+import jakarta.xml.bind.annotation.XmlElementWrapper;
+import jakarta.xml.bind.annotation.XmlElements;
+import jakarta.xml.bind.annotation.XmlEnum;
+import jakarta.xml.bind.annotation.XmlEnumValue;
+import jakarta.xml.bind.annotation.XmlID;
+import jakarta.xml.bind.annotation.XmlIDREF;
+import jakarta.xml.bind.annotation.XmlInlineBinaryData;
+import jakarta.xml.bind.annotation.XmlList;
+import jakarta.xml.bind.annotation.XmlMimeType;
+import jakarta.xml.bind.annotation.XmlMixed;
+import jakarta.xml.bind.annotation.XmlNs;
+import jakarta.xml.bind.annotation.XmlRegistry;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlSchema;
+import jakarta.xml.bind.annotation.XmlSchemaType;
+import jakarta.xml.bind.annotation.XmlSchemaTypes;
+import jakarta.xml.bind.annotation.XmlSeeAlso;
+import jakarta.xml.bind.annotation.XmlTransient;
+import jakarta.xml.bind.annotation.XmlType;
+import jakarta.xml.bind.annotation.XmlValue;
+import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+import org.springframework.aot.hint.TypeReference;
+import org.springframework.asm.ClassReader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.util.ReflectionUtils;
+
+import static org.apache.camel.spring.boot.aot.ReflectionHelper.applyIfMatch;
+import static org.apache.camel.spring.boot.aot.ReflectionHelper.getClassesByAnnotations;
+import static org.apache.camel.spring.boot.aot.RuntimeHintsHelper.registerClassHierarchy;
+
+final class JAXBRuntimeHints implements RuntimeHintsRegistrar {
+
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(JAXBRuntimeHints.class);
+
+ private static final List<Class<? extends Annotation>> JAXB_ROOT_ANNOTATIONS = List.of(
+ XmlRootElement.class, XmlType.class, XmlRegistry.class, XmlJavaTypeAdapter.class, XmlSeeAlso.class);
+
+ private static final List<Class<? extends Annotation>> JAXB_ANNOTATIONS = List.of(
+ XmlAccessorType.class,
+ XmlAnyAttribute.class,
+ XmlAnyElement.class,
+ XmlAttachmentRef.class,
+ XmlAttribute.class,
+ XmlElement.class,
+ XmlElementDecl.class,
+ XmlElementRef.class,
+ XmlElementRefs.class,
+ XmlElements.class,
+ XmlElementWrapper.class,
+ XmlEnum.class,
+ XmlEnumValue.class,
+ XmlID.class,
+ XmlIDREF.class,
+ XmlInlineBinaryData.class,
+ XmlList.class,
+ XmlMimeType.class,
+ XmlMixed.class,
+ XmlNs.class,
+ XmlRegistry.class,
+ XmlRootElement.class,
+ XmlSchema.class,
+ XmlSchemaType.class,
+ XmlSchemaTypes.class,
+ XmlSeeAlso.class,
+ XmlTransient.class,
+ XmlType.class,
+ XmlValue.class,
+ XmlJavaTypeAdapter.class,
+ XmlJavaTypeAdapters.class);
+
+ private static final List<String> NATIVE_PROXY_DEFINITIONS = List.of(
+ "org.glassfish.jaxb.core.marshaller.CharacterEscapeHandler",
+ "com.sun.xml.txw2.output.CharacterEscapeHandler",
+ "org.glassfish.jaxb.core.v2.schemagen.episode.Bindings",
+ "org.glassfish.jaxb.core.v2.schemagen.episode.SchemaBindings",
+ "org.glassfish.jaxb.core.v2.schemagen.episode.Klass",
+ "org.glassfish.jaxb.core.v2.schemagen.episode.Package",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Annotated",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Annotation",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Any",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Appinfo",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.AttrDecls",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.AttributeType",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ComplexContent",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ComplexExtension",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ComplexRestriction",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ComplexType",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ComplexTypeHost",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ComplexTypeModel",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ContentModelContainer",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Documentation",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Element",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ExplicitGroup",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.ExtensionType",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.FixedOrDefault",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Import",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.List",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.LocalAttribute",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.LocalElement",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.NestedParticle",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.NoFixedFacet",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Occurs",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Particle",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Redefinable",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Schema",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SchemaTop",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleContent",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleDerivation",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleExtension",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleRestriction",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleRestrictionModel",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleType",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.SimpleTypeHost",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.TopLevelAttribute",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.TopLevelElement",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.TypeDefParticle",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.TypeHost",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Union",
+ "org.glassfish.jaxb.runtime.v2.schemagen.xmlschema.Wildcard",
+ "com.sun.xml.txw2.TypedXmlWriter");
+ private static final List<String> JAXB_RUNTIME_CLASSES = List.of("org.glassfish.jaxb.runtime.v2.ContextFactory",
+ "com.sun.xml.internal.stream.XMLInputFactoryImpl",
+ "com.sun.xml.internal.stream.XMLOutputFactoryImpl",
+ "com.sun.org.apache.xpath.internal.functions.FuncNot",
+ "org.glassfish.jaxb.core.v2.model.nav.ReflectionNavigator",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.SingleElementLeafProperty",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.ArrayElementLeafProperty",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.SingleElementNodeProperty",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.SingleReferenceNodeProperty",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.SingleMapNodeProperty",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.ArrayElementNodeProperty",
+ "org.glassfish.jaxb.runtime.v2.runtime.property.ArrayReferenceNodeProperty",
+ "com.sun.org.apache.xerces.internal.impl.dv.xs.SchemaDVFactoryImpl", XmlAccessOrder.class.getName());
+
+
+ @Override
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ List<Class<?>> classes = getClassesByAnnotations(classLoader, JAXB_ROOT_ANNOTATIONS);
+ for (Class<?> c : classes) {
+ if (c.isAnnotationPresent(XmlSeeAlso.class)) {
+ XmlSeeAlso annotation = c.getAnnotation(XmlSeeAlso.class);
+ for (Class<?> type : annotation.value()) {
+ hints.reflection().registerType(type, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
+ }
+ }
+ applyIfMatch(c, XmlJavaTypeAdapter.class, XmlJavaTypeAdapter::value,
+ type -> hints.reflection().registerType(type, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS));
+ hints.reflection().registerType(c, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS);
+ }
+ boolean classDetected = false;
+ for (String className : getClassesFromIndexes(classLoader)) {
+ registerClassHierarchy(hints, classLoader, className, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS
+ );
+ classDetected = true;
+ }
+ if (classes.isEmpty() && !classDetected) {
+ return;
+ }
+ // Register all JAXB indexes
+ hints.resources().registerPattern("*/jaxb.index");
+
+ hints.reflection().registerTypeIfPresent(classLoader, "jakarta.xml.bind.annotation.W3CDomHandler",
+ MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
+ for (Class<?> c : JAXB_ANNOTATIONS) {
+ hints.reflection().registerType(c, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INVOKE_DECLARED_METHODS);
+ }
+ hints.proxies().registerJdkProxy(TypeReference.of(XmlSeeAlso.class), TypeReference.of("org.glassfish.jaxb.core.v2.model.annotation.Locatable"));
+ for (String className : NATIVE_PROXY_DEFINITIONS) {
+ hints.proxies().registerJdkProxy(TypeReference.of(className));
+ }
+ for (String className : JAXB_RUNTIME_CLASSES) {
+ hints.reflection().registerTypeIfPresent(classLoader, className,
+ MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS);
+ }
+ // Register the JAXB resource bundles
+ hints.reflection().registerTypeIfPresent(classLoader, "jakarta.xml.bind.Messages");
+ hints.resources().registerPattern("jakarta/xml/bind/Messages.properties");
+ hints.reflection().registerTypeIfPresent(classLoader, "jakarta.xml.bind.helpers.Messages");
+ hints.resources().registerPattern("jakarta/xml/bind/helpers/Messages.properties");
+ }
+
+ private static List<String> getClassesFromIndexes(ClassLoader classLoader) {
+ List<String> classNames = new ArrayList<>();
+ PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
+ try {
+ for (Resource resource : resolver.getResources("classpath*:**/jaxb.index")) {
+ String filename = resource.getFilename();
+ if (filename == null || filename.isBlank()) {
+ continue;
+ }
+ String packageName = getPackageName(resource, "jaxb.index");
+ if (packageName == null) {
+ LOG.debug("The package name could not be found for the resource {}", resource);
+ continue;
+ }
+ try (BufferedReader reader = new BufferedReader(new StringReader(resource.getContentAsString(StandardCharsets.UTF_8)))) {
+ String line = reader.readLine();
+ while (line != null) {
+ if (line.startsWith("#") || line.isBlank()) {
+ line = reader.readLine();
+ continue;
+ }
+ String className = "%s%s".formatted(packageName, line.trim());
+ LOG.debug("Found the class {} to register", className);
+ classNames.add(className);
+ line = reader.readLine();
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOG.debug("Could not load the JAXB indexes: {}", e.getMessage());
+ }
+ return classNames;
+ }
+
+ /**
+ * Give the package name of the given resource.
+ *
+ * @param resource the resource for which the package name is expected.
+ * @param fileName the name of file corresponding to the resource
+ * @return the package name if it could be found, {@code null} otherwise.
+ * @throws IOException an error occurs while trying to retrieve the package name.
+ */
+ private static String getPackageName(Resource resource, String fileName) throws IOException {
+ URL url = resource.getURL();
+ String protocol = url.getProtocol();
+ String packageName = null;
+ if ("jar".equals(protocol)) {
+ String path = url.getPath();
+ String suffix = ".jar!/";
+ int index = path.indexOf(suffix);
+ if (index == -1) {
+ LOG.trace("The jar suffix could not be found in {}", path);
+ } else {
+ packageName = path.substring(index + suffix.length(), path.length() - fileName.length());
+ }
+ } else if (resource.isFile()) {
+ File file = resource.getFile();
+ File[] files = file.getParentFile().listFiles((dir, name) -> name.endsWith(".class"));
+ if (files != null && files.length > 0) {
+ try (InputStream is = new FileInputStream(files[0])) {
+ ClassReader reader = new ClassReader(is);
+ String className = reader.getClassName();
+ int index = className.lastIndexOf('/');
+ if (index == -1) {
+ packageName = "";
+ } else {
+ packageName = className.substring(0, index + 1);
+ }
+ }
+ } else {
+ LOG.trace("No class file could be found in {}", file.getParentFile());
+ }
+ }
+ if (packageName != null) {
+ packageName = packageName.replace('/', '.');
+ }
+ return packageName;
+ }
+}
diff --git a/components-starter/camel-xml-jaxb-starter/src/main/java/org/apache/camel/xml/jaxb/springboot/graalvm/JAXBSubstitutions.java b/components-starter/camel-xml-jaxb-starter/src/main/java/org/apache/camel/xml/jaxb/springboot/graalvm/JAXBSubstitutions.java
new file mode 100644
index 00000000000..ed9435ae1ae
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/main/java/org/apache/camel/xml/jaxb/springboot/graalvm/JAXBSubstitutions.java
@@ -0,0 +1,101 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.xml.jaxb.springboot.graalvm;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.xml.bind.annotation.XmlSeeAlso;
+
+import com.oracle.svm.core.annotate.Alias;
+import com.oracle.svm.core.annotate.Substitute;
+import com.oracle.svm.core.annotate.TargetClass;
+import org.glassfish.jaxb.core.v2.model.annotation.Locatable;
+import org.glassfish.jaxb.runtime.v2.model.annotation.LocatableAnnotation;
+import org.glassfish.jaxb.runtime.v2.model.annotation.RuntimeInlineAnnotationReader;
+
+class JAXBSubstitutions {
+}
+
+@TargetClass(RuntimeInlineAnnotationReader.class)
+final class SubstituteRuntimeInlineAnnotationReader {
+
+ @Alias
+ private Map<Class<? extends Annotation>,Map<Package,Annotation>> packageCache;
+
+ @Substitute
+ public <A extends Annotation> A getFieldAnnotation(Class<A> annotation, Field field, Locatable srcPos) {
+ return field.getAnnotation(annotation);
+ }
+
+ @Substitute
+ public Annotation[] getAllFieldAnnotations(Field field, Locatable srcPos) {
+ return field.getAnnotations();
+ }
+
+ @Substitute
+ public <A extends Annotation> A getClassAnnotation(Class<A> a, Class clazz, Locatable srcPos) {
+ A ann = ((Class<?>) clazz).getAnnotation(a);
+ return (ann != null && ann.annotationType() == XmlSeeAlso.class) ? LocatableAnnotation.create(ann, srcPos) : ann;
+ }
+
+ @Substitute
+ public <A extends Annotation> A getMethodAnnotation(Class<A> annotation, Method method, Locatable srcPos) {
+ return method.getAnnotation(annotation);
+ }
+
+ @Substitute
+ public Annotation[] getAllMethodAnnotations(Method method, Locatable srcPos) {
+ return method.getAnnotations();
+ }
+
+ @Substitute
+ public <A extends Annotation> A getMethodParameterAnnotation(Class<A> annotation, Method method, int paramIndex,
+ Locatable srcPos) {
+ Annotation[] pa = method.getParameterAnnotations()[paramIndex];
+ for(Annotation a : pa) {
+ if (a.annotationType() == annotation)
+ return (A) a;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Substitute
+ public <A extends Annotation> A getPackageAnnotation(Class<A> a, Class clazz, Locatable srcPos) {
+ Package p = clazz.getPackage();
+ if (p == null) {
+ return null;
+ }
+
+ Map<Package, Annotation> cache = packageCache.get(a);
+ if (cache == null) {
+ cache = new HashMap<>();
+ packageCache.put(a, cache);
+ }
+ if (cache.containsKey(p)) {
+ return (A) cache.get(p);
+ } else {
+ A ann = p.getAnnotation(a);
+ cache.put(p, ann);
+ return ann;
+ }
+ }
+}
\ No newline at end of file
diff --git a/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/LICENSE.txt b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/LICENSE.txt
new file mode 100644
index 00000000000..6b0b1270ff0
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/LICENSE.txt
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
diff --git a/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/NOTICE.txt b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/NOTICE.txt
new file mode 100644
index 00000000000..2e215bf2e6b
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/NOTICE.txt
@@ -0,0 +1,11 @@
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Apache Camel distribution. ==
+ =========================================================================
+
+ This product includes software developed by
+ The Apache Software Foundation (http://www.apache.org/).
+
+ Please read the different LICENSE files present in the licenses directory of
+ this distribution.
diff --git a/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/spring.provides b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/spring.provides
new file mode 100644
index 00000000000..298e504419c
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/spring.provides
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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
+##
+## 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.
+## ---------------------------------------------------------------------------
+provides: camel-xml-jaxb
diff --git a/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/spring/aot.factories b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/spring/aot.factories
new file mode 100644
index 00000000000..fefd74c5e6a
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/main/resources/META-INF/spring/aot.factories
@@ -0,0 +1,2 @@
+org.springframework.aot.hint.RuntimeHintsRegistrar=\
+org.apache.camel.xml.jaxb.springboot.JAXBRuntimeHints
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/BeanScope.java
similarity index 58%
copy from core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
copy to components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/BeanScope.java
index ac3cb4d7e5b..6d1716bd875 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/BeanScope.java
@@ -14,25 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.spring.boot;
+package org.apache.camel.xml.jaxb.springboot;
-import org.apache.camel.component.properties.DefaultPropertiesParser;
-import org.apache.camel.component.properties.PropertiesLookup;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
+import jakarta.xml.bind.annotation.XmlEnum;
-class SpringPropertiesParser extends DefaultPropertiesParser {
+@XmlEnum
+public enum BeanScope {
+ Singleton,
+ Request,
+ Prototype;
- // Members
-
- @Autowired
- private Environment env;
-
- // Overridden
-
- @Override
- public String parseProperty(String key, String value, PropertiesLookup properties) {
- return env.getProperty(key);
+ BeanScope() {
}
-
}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/Book.java
similarity index 51%
copy from core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
copy to components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/Book.java
index ac3cb4d7e5b..72829dcdef5 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/Book.java
@@ -14,25 +14,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.spring.boot;
+package org.apache.camel.xml.jaxb.springboot;
-import org.apache.camel.component.properties.DefaultPropertiesParser;
-import org.apache.camel.component.properties.PropertiesLookup;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
+import jakarta.xml.bind.annotation.XmlAttribute;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlTransient;
+import jakarta.xml.bind.annotation.XmlType;
-class SpringPropertiesParser extends DefaultPropertiesParser {
+@XmlRootElement(name = "book")
+@XmlType(propOrder = { "id", "name", "date" })
+public class Book extends IdentifiedType {
+ private String name;
+ private String author;
- // Members
-
- @Autowired
- private Environment env;
+ @XmlElement(name = "title")
+ public void setName(String name) {
+ this.name = name;
+ }
- // Overridden
+ @XmlTransient
+ public void setAuthor(String author) {
+ this.author = author;
+ }
- @Override
- public String parseProperty(String key, String value, PropertiesLookup properties) {
- return env.getProperty(key);
+ public String getName() {
+ return name;
}
+ public String getAuthor() {
+ return author;
+ }
}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/IdentifiedType.java
similarity index 53%
copy from core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
copy to components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/IdentifiedType.java
index ac3cb4d7e5b..82fb5facf37 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/IdentifiedType.java
@@ -14,25 +14,36 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.spring.boot;
+package org.apache.camel.xml.jaxb.springboot;
-import org.apache.camel.component.properties.DefaultPropertiesParser;
-import org.apache.camel.component.properties.PropertiesLookup;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAttribute;
+import jakarta.xml.bind.annotation.XmlID;
+import jakarta.xml.bind.annotation.XmlType;
-class SpringPropertiesParser extends DefaultPropertiesParser {
+import org.apache.camel.spi.Metadata;
- // Members
+@XmlType(
+ name = "identifiedType"
+)
+@XmlAccessorType(XmlAccessType.FIELD)
+public abstract class IdentifiedType {
+ @XmlAttribute
+ @XmlID
+ @Metadata(
+ description = "The id of this node"
+ )
+ private String id;
- @Autowired
- private Environment env;
-
- // Overridden
+ public IdentifiedType() {
+ }
- @Override
- public String parseProperty(String key, String value, PropertiesLookup properties) {
- return env.getProperty(key);
+ public String getId() {
+ return this.id;
}
+ public void setId(String value) {
+ this.id = value;
+ }
}
diff --git a/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/JAXBRuntimeHintsTest.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/JAXBRuntimeHintsTest.java
new file mode 100644
index 00000000000..61aa398db01
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/JAXBRuntimeHintsTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.xml.jaxb.springboot;
+
+import jakarta.xml.bind.annotation.adapters.CollapsedStringAdapter;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for {@link JAXBRuntimeHints}.
+ */
+class JAXBRuntimeHintsTest {
+ private final RuntimeHints hints = new RuntimeHints();
+
+ @BeforeEach
+ void init() {
+ new JAXBRuntimeHints().registerHints(hints, getClass().getClassLoader());
+ }
+
+ @Test
+ void shouldRegisterHintsForJAXB() throws Exception {
+ assertThat(RuntimeHintsPredicates.resource().forResource("jakarta/xml/bind/Messages.properties")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.resource().forResource("org/apache/camel/spring/boot/aot/jaxb.index")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onType(Book.class)).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onMethod(Book.class, "getName")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onType(IdentifiedType.class)).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onMethod(IdentifiedType.class, "setId")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.resource().forResource("org/apache/camel/core/xml/util/jsse/jaxb.index")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onType(BeanScope.class)).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onMethod("org.glassfish.jaxb.core.v2.model.nav.ReflectionNavigator", "getInstance")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onType(USAddress.class)).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onType(CollapsedStringAdapter.class)).accepts(hints);
+ }
+}
diff --git a/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/USAddress.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/USAddress.java
new file mode 100644
index 00000000000..2e481659c5c
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/USAddress.java
@@ -0,0 +1,201 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.xml.jaxb.springboot;
+
+import java.math.BigDecimal;
+
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlAttribute;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlType;
+import jakarta.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "USAddress", propOrder = {
+ "name",
+ "street",
+ "city",
+ "state",
+ "zip"
+})
+public class USAddress {
+
+ @XmlElement(required = true)
+ protected String name;
+ @XmlElement(required = true)
+ protected String street;
+ @XmlElement(required = true)
+ protected String city;
+ @XmlElement(required = true)
+ protected String state;
+ @XmlElement(required = true)
+ protected BigDecimal zip;
+ @XmlAttribute
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ protected String country;
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the street property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getStreet() {
+ return street;
+ }
+
+ /**
+ * Sets the value of the street property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setStreet(String value) {
+ this.street = value;
+ }
+
+ /**
+ * Gets the value of the city property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCity() {
+ return city;
+ }
+
+ /**
+ * Sets the value of the city property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCity(String value) {
+ this.city = value;
+ }
+
+ /**
+ * Gets the value of the state property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getState() {
+ return state;
+ }
+
+ /**
+ * Sets the value of the state property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setState(String value) {
+ this.state = value;
+ }
+
+ /**
+ * Gets the value of the zip property.
+ *
+ * @return
+ * possible object is
+ * {@link java.math.BigDecimal }
+ *
+ */
+ public BigDecimal getZip() {
+ return zip;
+ }
+
+ /**
+ * Sets the value of the zip property.
+ *
+ * @param value
+ * allowed object is
+ * {@link java.math.BigDecimal }
+ *
+ */
+ public void setZip(BigDecimal value) {
+ this.zip = value;
+ }
+
+ /**
+ * Gets the value of the country property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCountry() {
+ if (country == null) {
+ return "US";
+ } else {
+ return country;
+ }
+ }
+
+ /**
+ * Sets the value of the country property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCountry(String value) {
+ this.country = value;
+ }
+
+}
diff --git a/components-starter/camel-xml-jaxb-starter/src/test/resources/org/apache/camel/xml/jaxb/springboot/jaxb.index b/components-starter/camel-xml-jaxb-starter/src/test/resources/org/apache/camel/xml/jaxb/springboot/jaxb.index
new file mode 100644
index 00000000000..f56c7c8af9a
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/resources/org/apache/camel/xml/jaxb/springboot/jaxb.index
@@ -0,0 +1 @@
+BeanScope
\ No newline at end of file
diff --git a/components-starter/pom.xml b/components-starter/pom.xml
index a09ce8228c7..3d82859b5e1 100644
--- a/components-starter/pom.xml
+++ b/components-starter/pom.xml
@@ -417,6 +417,7 @@
<module>camel-workday-starter</module>
<module>camel-xchange-starter</module>
<module>camel-xj-starter</module>
+ <module>camel-xml-jaxb-starter</module>
<module>camel-xml-jaxp-starter</module>
<module>camel-xmlsecurity-starter</module>
<module>camel-xmpp-starter</module>
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java
index b6d77a32b09..2934b2a00c0 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelAutoConfiguration.java
@@ -23,7 +23,6 @@ import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.ConsumerTemplate;
-import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.FluentProducerTemplate;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.RuntimeCamelException;
@@ -39,6 +38,7 @@ import org.apache.camel.spi.CliConnectorFactory;
import org.apache.camel.spi.PackageScanClassResolver;
import org.apache.camel.spi.PackageScanResourceResolver;
import org.apache.camel.spi.StartupStepRecorder;
+import org.apache.camel.spring.boot.aot.CamelRuntimeHints;
import org.apache.camel.spring.spi.ApplicationContextBeanRepository;
import org.apache.camel.spring.spi.CamelBeanPostProcessor;
import org.apache.camel.support.DefaultRegistry;
@@ -56,6 +56,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Role;
import org.springframework.core.OrderComparator;
@@ -63,6 +64,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
+@ImportRuntimeHints(CamelRuntimeHints.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(CamelConfigurationProperties.class)
@Import(TypeConversionConfiguration.class)
@@ -279,8 +281,8 @@ public class CamelAutoConfiguration {
@Bean
@ConditionalOnMissingBean(PropertiesParser.class)
- PropertiesParser propertiesParser() {
- return new SpringPropertiesParser();
+ PropertiesParser propertiesParser(Environment env) {
+ return new SpringPropertiesParser(env);
}
// We explicitly declare the destroyMethod to be "" as the Spring @Bean
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java
index a912b42bdab..e6754385ebc 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/CamelSpringBootApplicationController.java
@@ -67,8 +67,10 @@ public class CamelSpringBootApplicationController {
run();
}
+ // The method cannot to be private to prevent a failure at startup in AOT mode
+ // Refer to https://github.com/spring-projects/spring-framework/pull/30654 for more details
@PreDestroy
- private void destroy() {
+ void destroy() {
main.completed();
}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
index ac3cb4d7e5b..d05f5bd2e34 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
@@ -18,15 +18,16 @@ package org.apache.camel.spring.boot;
import org.apache.camel.component.properties.DefaultPropertiesParser;
import org.apache.camel.component.properties.PropertiesLookup;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
class SpringPropertiesParser extends DefaultPropertiesParser {
// Members
+ private final Environment env;
- @Autowired
- private Environment env;
+ SpringPropertiesParser(Environment env) {
+ this.env = env;
+ }
// Overridden
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/CamelRuntimeHints.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/CamelRuntimeHints.java
new file mode 100644
index 00000000000..89c61475d48
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/CamelRuntimeHints.java
@@ -0,0 +1,98 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.spring.boot.aot;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import static org.apache.camel.spring.boot.aot.RuntimeHintsHelper.registerClassHierarchy;
+
+/**
+ * {@code CamelRuntimeHints} provide the basic hints for the native compilation of a Camel application.
+ */
+public final class CamelRuntimeHints implements RuntimeHintsRegistrar {
+
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(CamelRuntimeHints.class);
+
+ @Override
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ // Give access to the catalog
+ hints.resources().registerPattern("org/apache/camel/main/*.properties");
+ // Register all the camel services
+ registerCamelServices(hints, classLoader);
+ // Register collections
+ hints.reflection().registerType(java.util.List.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INVOKE_PUBLIC_METHODS);
+ hints.reflection().registerType(java.util.Collection.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INVOKE_PUBLIC_METHODS);
+ }
+
+ /**
+ * Register all the Camel services that could be found in the given classloader.
+ *
+ * @param hints the hints contributed so far for the deployment unit
+ * @param classLoader the ClassLoader to load classpath resources with,
+ * or {@code null} for using the thread context class loader
+ * at the time of actual resource access
+ */
+ private static void registerCamelServices(RuntimeHints hints, ClassLoader classLoader) {
+ hints.resources().registerPattern("META-INF/services/org/apache/camel/*");
+ PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
+ try {
+ for (Resource resource : resolver.getResources("classpath*:META-INF/services/org/apache/camel/**")) {
+ String filename = resource.getFilename();
+ if (filename == null || filename.isBlank() || filename.endsWith(".properties")) {
+ continue;
+ }
+ try (BufferedReader reader = new BufferedReader(new StringReader(resource.getContentAsString(StandardCharsets.UTF_8)))) {
+ String line = reader.readLine();
+ String prefixClass = "class=";
+ while (line != null) {
+ if (line.startsWith("#") || line.isBlank()) {
+ line = reader.readLine();
+ continue;
+ }
+ String className = line.trim();
+ if (line.startsWith(prefixClass)) {
+ className = line.substring(prefixClass.length());
+ }
+ LOG.debug("Found the class {} to register", className);
+ registerClassHierarchy(hints, classLoader, className, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
+ MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS,
+ MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INTROSPECT_PUBLIC_METHODS);
+ line = reader.readLine();
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOG.debug("Could not load the Camel services: {}", e.getMessage());
+ }
+ }
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/ReflectionHelper.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/ReflectionHelper.java
new file mode 100644
index 00000000000..33e1df9de5c
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/ReflectionHelper.java
@@ -0,0 +1,238 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.spring.boot.aot;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Parameter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.ClassMetadata;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.core.type.filter.TypeFilter;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * {@code ReflectionHelper} utility class providing methods needed for the AOT mode.
+ */
+public final class ReflectionHelper {
+
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(ReflectionHelper.class);
+
+ private ReflectionHelper() {
+
+ }
+
+ /**
+ * Apply a specific action anytime the annotation is found in the class according to the type of target supported
+ * by the annotation.
+ *
+ * @param c the target class
+ * @param a the target type of annotation
+ * @param getter the method allowing to extract the excepted values from the annotation
+ * @param onMatch the action to perform in case of a match
+ * @param <A> the type of the target annotation
+ * @param <T> the type of the content extracted from the annotation found
+ */
+ public static <A extends Annotation, T> void applyIfMatch(Class<?> c, Class<A> a, Function<A, T> getter,
+ Consumer<T> onMatch) {
+ Set<ElementType> targets = null;
+ if (a.isAnnotationPresent(Target.class)) {
+ targets = Collections.newSetFromMap(new EnumMap<>(ElementType.class));
+ targets.addAll(Arrays.asList(a.getAnnotation(Target.class).value()));
+ }
+ if ((targets == null || targets.contains(ElementType.TYPE)) && c.isAnnotationPresent(a)) {
+ onMatch.accept(getter.apply(c.getAnnotation(a)));
+ }
+ boolean checkConstructors = targets == null || targets.contains(ElementType.CONSTRUCTOR);
+ boolean checkParameters = targets == null || targets.contains(ElementType.PARAMETER);
+ if (checkConstructors || checkParameters) {
+ for (Constructor<?> constructor : c.getDeclaredConstructors()) {
+ if (checkConstructors && constructor.isAnnotationPresent(a)) {
+ onMatch.accept(getter.apply(constructor.getAnnotation(a)));
+ }
+ if (checkParameters) {
+ for (Parameter parameter : constructor.getParameters()) {
+ if (parameter.isAnnotationPresent(a)) {
+ onMatch.accept(getter.apply(parameter.getAnnotation(a)));
+ }
+ }
+ }
+ }
+ }
+ if (targets == null || targets.contains(ElementType.FIELD)) {
+ ReflectionUtils.doWithFields(c,
+ field -> onMatch.accept(getter.apply(field.getAnnotation(a))), field -> field.isAnnotationPresent(a));
+ }
+ boolean checkMethods = targets == null || targets.contains(ElementType.METHOD);
+ if (checkMethods || checkParameters) {
+ ReflectionUtils.doWithMethods(
+ c,
+ method -> {
+ if (checkMethods && method.isAnnotationPresent(a)) {
+ onMatch.accept(getter.apply(method.getAnnotation(a)));
+ }
+ if (checkParameters) {
+ for (Parameter parameter : method.getParameters()) {
+ if (parameter.isAnnotationPresent(a)) {
+ onMatch.accept(getter.apply(parameter.getAnnotation(a)));
+ }
+ }
+ }
+ }
+ );
+ }
+ }
+
+ /**
+ * Give all the classes available in the given class loader that are annotated with at least one of the annotations.
+ *
+ * @param classLoader the class loader from which the classes to find are loaded
+ * @param annotations the target annotations
+ * @return the list of classes that are annotated with at least one of the annotations.
+ */
+ public static List<Class<?>> getClassesByAnnotations(ClassLoader classLoader, List<Class<? extends Annotation>> annotations) {
+ return getClassesByFilters(classLoader, annotations.stream().map(AnnotationTypeFilter::new).collect(Collectors.toList()));
+ }
+
+ /**
+ * Give all the classes available in the given class loader that match with at least one of the filters.
+ *
+ * @param classLoader the class loader from which the classes to find are loaded
+ * @param includeFilters the filters to apply the classes found
+ * @return a list of classes that match with at least one of the given filters
+ */
+ public static List<Class<?>> getClassesByFilters(ClassLoader classLoader, List<TypeFilter> includeFilters) {
+ ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false) {
+ @Override
+ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
+ return true;
+ }
+ };
+ provider.setResourceLoader(new PathMatchingResourcePatternResolver(classLoader));
+ provider.setMetadataReaderFactory(new SafeMetadataReaderFactory(provider.getMetadataReaderFactory()));
+ provider.addExcludeFilter(
+ (metadata, factory) -> {
+ String className = metadata.getClassMetadata().getClassName();
+ return className.startsWith("org.springframework.") || className.startsWith("java.") || className.startsWith("jakarta.");
+ });
+ for (TypeFilter filter : includeFilters) {
+ provider.addIncludeFilter(filter);
+ }
+ return provider.findCandidateComponents("")
+ .stream()
+ .map(b -> asClass(b, classLoader))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Convert the given bean into a class.
+ * @param bean the bean to convert.
+ * @param classLoader the classloader from which the class of the bean is loaded
+ * @return the class corresponding to the bean if it could be found, {@code null} otherwise.
+ */
+ private static Class<?> asClass(BeanDefinition bean, ClassLoader classLoader) {
+ String beanClassName = bean.getBeanClassName();
+ if (beanClassName == null) {
+ LOG.debug("The name of the class corresponding to the bean '{}' could not be found", bean);
+ } else {
+ try {
+ return ClassUtils.forName(beanClassName, classLoader);
+ } catch (ClassNotFoundException | NoClassDefFoundError e) {
+ LOG.debug("The class corresponding to the bean '{}' could not be found: {}", bean, e.getMessage());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@code SafeMetadataReaderFactory} is a specific {@link MetadataReaderFactory} whose methods never throw any
+ * exceptions, if an error occurs while calling the underlying {@link MetadataReaderFactory} a debug message is
+ * logged and the default result is returned.
+ */
+ private static class SafeMetadataReaderFactory implements MetadataReaderFactory {
+
+ /**
+ * The instance of the default result in case of an error.
+ */
+ private static final MetadataReader DEFAULT = new MetadataReader() {
+ @Override
+ public Resource getResource() {
+ return new ByteArrayResource(new byte[0]);
+ }
+
+ @Override
+ public ClassMetadata getClassMetadata() {
+ return AnnotationMetadata.introspect(Object.class);
+ }
+
+ @Override
+ public AnnotationMetadata getAnnotationMetadata() {
+ return AnnotationMetadata.introspect(Object.class);
+ }
+ };
+ private final MetadataReaderFactory delegate;
+
+ SafeMetadataReaderFactory(MetadataReaderFactory delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public MetadataReader getMetadataReader(String className) {
+ try {
+ return delegate.getMetadataReader(className);
+ } catch (Exception | NoClassDefFoundError e) {
+ LOG.debug("Could not get the metadata of the class {}", className);
+ }
+ return DEFAULT;
+ }
+
+ @Override
+ public MetadataReader getMetadataReader(Resource resource) {
+ try {
+ return delegate.getMetadataReader(resource);
+ } catch (Exception | NoClassDefFoundError e) {
+ LOG.debug("Could not get the metadata of the resource {}", resource);
+ }
+ return DEFAULT;
+ }
+ }
+}
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/RuntimeHintsHelper.java b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/RuntimeHintsHelper.java
new file mode 100644
index 00000000000..d759cc8b3f4
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/RuntimeHintsHelper.java
@@ -0,0 +1,71 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.spring.boot.aot;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.util.ClassUtils;
+
+public final class RuntimeHintsHelper {
+
+ /**
+ * The logger.
+ */
+ private static final Logger LOG = LoggerFactory.getLogger(RuntimeHintsHelper.class);
+
+ private RuntimeHintsHelper() {
+ }
+
+
+ /**
+ * Register the given class and all its parent classes by applying the given member categories.
+ *
+ * @param hints the hints contributed so far for the deployment unit
+ * @param classLoader the ClassLoader to load classpath resources with,
+ * or {@code null} for using the thread context class loader
+ * at the time of actual resource access
+ * @param className the name of the class to register
+ * @param memberCategories the member categories to apply
+ */
+ public static void registerClassHierarchy(RuntimeHints hints, ClassLoader classLoader, String className,
+ MemberCategory... memberCategories) {
+ try {
+ registerClassHierarchy(hints, ClassUtils.forName(className, classLoader), memberCategories);
+ } catch (ClassNotFoundException | NoClassDefFoundError e) {
+ LOG.debug("The class {} cannot be found", className);
+ }
+ }
+
+ /**
+ * Register the given class and all its parent classes by applying the given member categories.
+ *
+ * @param hints the hints contributed so far for the deployment unit
+ * @param clazz the class to register
+ * @param memberCategories the member categories to apply
+ */
+ public static void registerClassHierarchy(RuntimeHints hints, Class<?> clazz, MemberCategory... memberCategories) {
+ if (clazz.isInterface() || clazz.isArray()) {
+ return;
+ }
+ while (clazz != Object.class) {
+ hints.reflection().registerType(clazz, memberCategories);
+ clazz = clazz.getSuperclass();
+ }
+ }
+}
diff --git a/core/camel-spring-boot/src/main/resources/META-INF/native-image/org.apache.camel.springboot/camel-spring-boot/native-image.properties b/core/camel-spring-boot/src/main/resources/META-INF/native-image/org.apache.camel.springboot/camel-spring-boot/native-image.properties
new file mode 100644
index 00000000000..a974103baf7
--- /dev/null
+++ b/core/camel-spring-boot/src/main/resources/META-INF/native-image/org.apache.camel.springboot/camel-spring-boot/native-image.properties
@@ -0,0 +1 @@
+Args = -H:+AddAllCharsets
\ No newline at end of file
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/aot/CamelRuntimeHintsTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/aot/CamelRuntimeHintsTest.java
new file mode 100644
index 00000000000..d942ac47c30
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/aot/CamelRuntimeHintsTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.spring.boot.aot;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.language.simple.SimpleLanguage;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for {@link CamelRuntimeHints}.
+ */
+class CamelRuntimeHintsTest {
+
+ private final RuntimeHints hints = new RuntimeHints();
+
+ @BeforeEach
+ void init() {
+ new CamelRuntimeHints().registerHints(hints, getClass().getClassLoader());
+ }
+
+ @Test
+ void shouldRegisterHintsForCamelServices() throws Exception {
+ assertThat(RuntimeHintsPredicates.resource().forResource("META-INF/services/org/apache/camel/language/simple")).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onConstructor(SimpleLanguage.class.getConstructor())).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onMethod(SimpleLanguage.class.getMethod("init"))).accepts(hints);
+ assertThat(RuntimeHintsPredicates.reflection().onMethod(SimpleLanguage.class.getMethod("setCamelContext", CamelContext.class))).accepts(hints);
+ }
+
+ @Test
+ void shouldRegisterHintsForCamelCatalog() {
+ assertThat(RuntimeHintsPredicates.resource().forResource("org/apache/camel/main/components.properties")).accepts(hints);
+ }
+}
diff --git a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/aot/ReflectionHelperTest.java b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/aot/ReflectionHelperTest.java
new file mode 100644
index 00000000000..c01e26e115f
--- /dev/null
+++ b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/aot/ReflectionHelperTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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
+ *
+ * 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.apache.camel.spring.boot.aot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Unit tests for {@link ReflectionHelperTest}.
+ */
+class ReflectionHelperTest {
+
+ private final AtomicInteger counter = new AtomicInteger();
+
+ @Test
+ void shouldAlwaysApply() {
+ ReflectionHelper.applyIfMatch(Foo.class, All.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isEqualTo(11);
+ }
+
+ @Test
+ void shouldNeverApply() {
+ ReflectionHelper.applyIfMatch(Foo.class, None.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isZero();
+ }
+
+ @Test
+ void shouldApplyToClassOnly() {
+ ReflectionHelper.applyIfMatch(Foo.class, OnlyType.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isEqualTo(1);
+ }
+
+ @Test
+ void shouldApplyToOneSpecificConstructor() {
+ ReflectionHelper.applyIfMatch(Foo.class, OnlyConstructor.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isEqualTo(1);
+ }
+
+ @Test
+ void shouldApplyToOneSpecificField() {
+ ReflectionHelper.applyIfMatch(Foo.class, OnlyField.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isEqualTo(1);
+ }
+
+ @Test
+ void shouldApplyToOneSpecificMethod() {
+ ReflectionHelper.applyIfMatch(Foo.class, OnlyMethod.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isEqualTo(1);
+ }
+
+ @Test
+ void shouldApplyToSpecificParameters() {
+ ReflectionHelper.applyIfMatch(Foo.class, OnlyParameter.class, a -> null, x -> counter.incrementAndGet());
+ assertThat(counter.get()).isEqualTo(2);
+ }
+
+ @OnlyType
+ @All
+ public static class Foo {
+
+ @All
+ private String someField1;
+ @OnlyField
+ @All
+ private String someField2;
+
+ @All
+ private Foo() {}
+
+ @OnlyConstructor
+ @All
+ private Foo(@OnlyParameter @All String someParam1, @All String someParam2) {}
+
+ @All
+ private void someMethod1(@All String someParam1, @All @OnlyParameter String someParam2) {}
+
+ @OnlyMethod
+ @All
+ private void someMethod2() {}
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface All {
+
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface None {
+
+ }
+
+ @Target(ElementType.TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface OnlyType {
+
+ }
+
+ @Target(ElementType.FIELD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface OnlyField {
+
+ }
+
+ @Target(ElementType.CONSTRUCTOR)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface OnlyConstructor {
+
+ }
+
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface OnlyMethod {
+
+ }
+
+ @Target(ElementType.PARAMETER)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface OnlyParameter {
+
+ }
+}
diff --git a/dsl-starter/camel-xml-jaxb-dsl-starter/pom.xml b/dsl-starter/camel-xml-jaxb-dsl-starter/pom.xml
index aec33256ef6..e7527e5b0d7 100644
--- a/dsl-starter/camel-xml-jaxb-dsl-starter/pom.xml
+++ b/dsl-starter/camel-xml-jaxb-dsl-starter/pom.xml
@@ -49,5 +49,10 @@
<artifactId>camel-xml-jaxb-dsl</artifactId>
<version>${camel-version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.springboot</groupId>
+ <artifactId>camel-xml-jaxb-starter</artifactId>
+ <version>${camel-version}</version>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java b/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/java/org/apache/camel/dsl/xml/jaxb/springboot/aot/XMLDSLRuntimeHints.java
similarity index 59%
copy from core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
copy to dsl-starter/camel-xml-jaxb-dsl-starter/src/main/java/org/apache/camel/dsl/xml/jaxb/springboot/aot/XMLDSLRuntimeHints.java
index ac3cb4d7e5b..75abf68a13c 100644
--- a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/SpringPropertiesParser.java
+++ b/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/java/org/apache/camel/dsl/xml/jaxb/springboot/aot/XMLDSLRuntimeHints.java
@@ -14,25 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.spring.boot;
+package org.apache.camel.dsl.xml.jaxb.springboot.aot;
-import org.apache.camel.component.properties.DefaultPropertiesParser;
-import org.apache.camel.component.properties.PropertiesLookup;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.env.Environment;
+import java.util.ArrayList;
-class SpringPropertiesParser extends DefaultPropertiesParser {
-
- // Members
-
- @Autowired
- private Environment env;
-
- // Overridden
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.RuntimeHintsRegistrar;
+final class XMLDSLRuntimeHints implements RuntimeHintsRegistrar {
@Override
- public String parseProperty(String key, String value, PropertiesLookup properties) {
- return env.getProperty(key);
+ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
+ hints.reflection().registerType(ArrayList.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
-
}
diff --git a/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/resources/META-INF/spring/aot.factories b/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/resources/META-INF/spring/aot.factories
new file mode 100644
index 00000000000..0c240e4c506
--- /dev/null
+++ b/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/resources/META-INF/spring/aot.factories
@@ -0,0 +1,2 @@
+org.springframework.aot.hint.RuntimeHintsRegistrar=\
+org.apache.camel.dsl.xml.jaxb.springboot.aot.XMLDSLRuntimeHints
diff --git a/pom.xml b/pom.xml
index 6c82f016d13..b933255d8ac 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,6 +109,7 @@
<!-- Spring-Boot target version -->
<spring-boot-version>3.1.0</spring-boot-version>
+ <spring-version>6.0.9</spring-version>
<!-- Camel target version -->
<camel-version>4.0.0-SNAPSHOT</camel-version>
@@ -119,6 +120,7 @@
<arquillian-version>1.7.0.Alpha10</arquillian-version>
<avro-version>1.11.0</avro-version>
<groovy-version>4.0.12</groovy-version>
+ <graal-sdk-version>22.3.2</graal-sdk-version>
<jakarta-jaxb-version>4.0.0</jakarta-jaxb-version>
<jaxb-version>2.3.0</jaxb-version>
<maven-compiler-plugin-version>3.11.0</maven-compiler-plugin-version>