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 @@
+
+
+
+ 4.0.0
+
+ org.apache.camel.springboot
+ components-starter
+ 4.0.0-SNAPSHOT
+
+ camel-xml-jaxb-starter
+ jar
+ Spring-Boot Starter for Camel JAXB Support
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring-boot-version}
+
+
+ org.apache.camel
+ camel-xml-jaxb
+ ${camel-version}
+
+
+ org.graalvm.sdk
+ graal-sdk
+ ${graal-sdk-version}
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-version}
+ test
+
+
+ org.springframework
+ spring-core-test
+ ${spring-version}
+ test
+
+
+
+ org.apache.camel.springboot
+ camel-core-starter
+
+
+
+
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> JAXB_ROOT_ANNOTATIONS = List.of(
+ XmlRootElement.class, XmlType.class, XmlRegistry.class, XmlJavaTypeAdapter.class, XmlSeeAlso.class);
+
+ private static final List> 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 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 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> 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 getClassesFromIndexes(ClassLoader classLoader) {
+ List 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..b44441ff002
--- /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,Map> packageCache;
+
+ @Substitute
+ public A getFieldAnnotation(Class annotation, Field field, Locatable srcPos) {
+ return field.getAnnotation(annotation);
+ }
+
+ @Substitute
+ public Annotation[] getAllFieldAnnotations(Field field, Locatable srcPos) {
+ return field.getAnnotations();
+ }
+
+ @Substitute
+ public A getClassAnnotation(Class 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 getMethodAnnotation(Class annotation, Method method, Locatable srcPos) {
+ return method.getAnnotation(annotation);
+ }
+
+ @Substitute
+ public Annotation[] getAllMethodAnnotations(Method method, Locatable srcPos) {
+ return method.getAnnotations();
+ }
+
+ @Substitute
+ public A getMethodParameterAnnotation(Class 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 getPackageAnnotation(Class a, Class clazz, Locatable srcPos) {
+ Package p = clazz.getPackage();
+ if (p == null) {
+ return null;
+ }
+
+ Map 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;
+ }
+ }
+}
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/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/BeanScope.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/BeanScope.java
new file mode 100644
index 00000000000..6d1716bd875
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/BeanScope.java
@@ -0,0 +1,29 @@
+/*
+ * 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.XmlEnum;
+
+@XmlEnum
+public enum BeanScope {
+ Singleton,
+ Request,
+ Prototype;
+
+ BeanScope() {
+ }
+}
diff --git a/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/Book.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/Book.java
new file mode 100644
index 00000000000..72829dcdef5
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/Book.java
@@ -0,0 +1,48 @@
+/*
+ * 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.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;
+
+@XmlRootElement(name = "book")
+@XmlType(propOrder = { "id", "name", "date" })
+public class Book extends IdentifiedType {
+ private String name;
+ private String author;
+
+ @XmlElement(name = "title")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @XmlTransient
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+}
diff --git a/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/IdentifiedType.java b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/IdentifiedType.java
new file mode 100644
index 00000000000..82fb5facf37
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/java/org/apache/camel/xml/jaxb/springboot/IdentifiedType.java
@@ -0,0 +1,49 @@
+/*
+ * 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.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;
+
+import org.apache.camel.spi.Metadata;
+
+@XmlType(
+ name = "identifiedType"
+)
+@XmlAccessorType(XmlAccessType.FIELD)
+public abstract class IdentifiedType {
+ @XmlAttribute
+ @XmlID
+ @Metadata(
+ description = "The id of this node"
+ )
+ private String id;
+
+ public IdentifiedType() {
+ }
+
+ 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..31d2b53009d
--- /dev/null
+++ b/components-starter/camel-xml-jaxb-starter/src/test/resources/org/apache/camel/xml/jaxb/springboot/jaxb.index
@@ -0,0 +1 @@
+BeanScope
diff --git a/components-starter/pom.xml b/components-starter/pom.xml
index 0b7845b722b..e795cbae8be 100644
--- a/components-starter/pom.xml
+++ b/components-starter/pom.xml
@@ -411,6 +411,7 @@
camel-workday-starter
camel-xchange-starter
camel-xj-starter
+ camel-xml-jaxb-starter
camel-xml-jaxp-starter
camel-xmlsecurity-starter
camel-xmpp-starter
diff --git a/core/camel-spring-boot/src/main/docs/spring-boot.adoc b/core/camel-spring-boot/src/main/docs/spring-boot.adoc
index 15b40d3c424..8af85a735b1 100644
--- a/core/camel-spring-boot/src/main/docs/spring-boot.adoc
+++ b/core/camel-spring-boot/src/main/docs/spring-boot.adoc
@@ -454,3 +454,17 @@ public class MyApplicationTest {
}
----
+
+== Camel Spring Boot Native
+
+One of the most interesting features added to Spring Boot 3 is the support of GraalVM Native Image which allows you to reduce
+significantly the memory footprint and the startup time of your application. Those improvements are only possible thanks
+to the Ahead-Of-Time (AOT) compilation that relies on a closed-world assumption which means that everything needs to be
+known at build type, all dynamic aspects included such as reflection, JNI, Proxy, and resources loading from a ClassLoader.
+
+For now, only Camel routes written using the Java, XML, and/or YAML DSL with basic components that don't rely on dynamic aspects to work are covered out of the box. For other components,
+you will need to provide GraalVM some hints to let it know all the dynamic aspects needed by your application either by
+implementing your custom https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.advanced.custom-hints[`RuntimeHintsRegistrar`]
+or by providing GraalVM JSON hint files that can be generated by the https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.advanced.using-the-tracing-agent[Tracing Agent].
+
+For more details about `GraalVM Native Image Support` in Spring Boot please refer to https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html
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 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.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.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.Environment;
import org.springframework.core.env.MutablePropertySources;
+@ImportRuntimeHints(CamelRuntimeHints.class)
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(CamelConfigurationProperties.class)
@Import(TypeConversionConfiguration.class)
@@ -279,8 +281,8 @@ ConsumerTemplate consumerTemplate(CamelContext camelContext,
@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..7f9faa134fd 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 void blockMainThread() {
run();
}
+ // The method cannot to be private to prevent a failure at startup in native 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 @@
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..b6a4aadc05c
--- /dev/null
+++ b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/aot/ReflectionHelper.java
@@ -0,0 +1,239 @@
+/*
+ * 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 native 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 which can be either {@link ElementType#TYPE}, {@link ElementType#CONSTRUCTOR},
+ * {@link ElementType#METHOD}, {@link ElementType#FIELD}, or {@link ElementType#PARAMETER}.
+ *
+ * @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 the type of the target annotation
+ * @param the type of the content extracted from the annotation found
+ */
+ public static void applyIfMatch(Class> c, Class a, Function getter,
+ Consumer onMatch) {
+ Set 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> getClassesByAnnotations(ClassLoader classLoader, List> 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> getClassesByFilters(ClassLoader classLoader, List 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 definition into a class.
+ * @param bean the bean definition to convert.
+ * @param classLoader the classloader from which the class of the bean is loaded
+ * @return the class corresponding to the bean definition 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..6e7d06f2fe9
--- /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
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 @@
camel-xml-jaxb-dsl
${camel-version}
+
+ org.apache.camel.springboot
+ camel-xml-jaxb-starter
+ ${camel-version}
+
\ No newline at end of file
diff --git a/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/java/org/apache/camel/dsl/xml/jaxb/springboot/aot/XMLDSLRuntimeHints.java b/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/java/org/apache/camel/dsl/xml/jaxb/springboot/aot/XMLDSLRuntimeHints.java
new file mode 100644
index 00000000000..75abf68a13c
--- /dev/null
+++ b/dsl-starter/camel-xml-jaxb-dsl-starter/src/main/java/org/apache/camel/dsl/xml/jaxb/springboot/aot/XMLDSLRuntimeHints.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dsl.xml.jaxb.springboot.aot;
+
+import java.util.ArrayList;
+
+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 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 a27fc89cccd..a2a463af200 100644
--- a/pom.xml
+++ b/pom.xml
@@ -119,6 +119,7 @@
1.7.0.Alpha10
1.11.0
4.0.12
+ 22.3.2
4.0.0
2.3.0
3.11.0