org.apache.camel.xml.jaxb.springboot.JAXBRuntimeHints Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of camel-xml-jaxb-starter Show documentation
Show all versions of camel-xml-jaxb-starter Show documentation
Spring-Boot Starter for Camel JAXB Support
The newest version!
/*
* 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.INVOKE_PUBLIC_METHODS,
MemberCategory.DECLARED_FIELDS));
hints.reflection().registerType(c, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_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, MemberCategory.INVOKE_PUBLIC_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, MemberCategory.INVOKE_PUBLIC_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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy