All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.jaxb.JAXBContext Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     Marcel Valovy - 2.6 - added ci unmarshalling & BV in JAXB
//     Dmitry Kornilov - 2.6.1 - BeanValidationHelper refactoring
package org.eclipse.persistence.jaxb;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.xml.namespace.QName;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.transform.Source;

import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.PropertyException;
import jakarta.xml.bind.SchemaOutputResolver;
import jakarta.xml.bind.ValidationEvent;
import jakarta.xml.bind.ValidationEventHandler;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;

import org.eclipse.persistence.Version;
import org.eclipse.persistence.core.queries.CoreAttributeGroup;
import org.eclipse.persistence.core.sessions.CoreProject;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.ConversionException;
import org.eclipse.persistence.exceptions.JAXBException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.jaxb.JAXBSchemaOutputResolver;
import org.eclipse.persistence.internal.jaxb.JaxbClassLoader;
import org.eclipse.persistence.internal.jaxb.ObjectGraphImpl;
import org.eclipse.persistence.internal.jaxb.WrappedValue;
import org.eclipse.persistence.internal.jaxb.json.schema.JsonSchemaGenerator;
import org.eclipse.persistence.internal.jaxb.json.schema.model.JsonSchema;
import org.eclipse.persistence.internal.jaxb.many.ManyValue;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.Root;
import org.eclipse.persistence.internal.oxm.XMLConversionManager;
import org.eclipse.persistence.internal.oxm.XPathFragment;
import org.eclipse.persistence.internal.oxm.mappings.ChoiceCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.ChoiceObjectMapping;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.schema.SchemaModelGenerator;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.jaxb.compiler.Generator;
import org.eclipse.persistence.jaxb.compiler.MarshalCallback;
import org.eclipse.persistence.jaxb.compiler.UnmarshalCallback;
import org.eclipse.persistence.jaxb.javamodel.JavaClass;
import org.eclipse.persistence.jaxb.javamodel.reflection.AnnotationHelper;
import org.eclipse.persistence.jaxb.javamodel.reflection.JavaClassImpl;
import org.eclipse.persistence.jaxb.javamodel.reflection.JavaModelImpl;
import org.eclipse.persistence.jaxb.javamodel.reflection.JavaModelInputImpl;
import org.eclipse.persistence.jaxb.json.JsonSchemaOutputResolver;
import org.eclipse.persistence.jaxb.xmlmodel.JavaType;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.JavaTypes;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.eclipse.persistence.logging.LogLevel;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.oxm.MediaType;
import org.eclipse.persistence.oxm.NamespaceResolver;
import org.eclipse.persistence.oxm.XMLContext;
import org.eclipse.persistence.oxm.XMLField;
import org.eclipse.persistence.oxm.XMLLogin;
import org.eclipse.persistence.oxm.XMLMarshaller;
import org.eclipse.persistence.oxm.XMLUnmarshaller;
import org.eclipse.persistence.oxm.platform.SAXPlatform;
import org.eclipse.persistence.oxm.platform.XMLPlatform;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.SessionEventListener;

import static org.eclipse.persistence.jaxb.javamodel.Helper.getQualifiedJavaTypeName;

/**
 * 

Purpose:Provide a EclipseLink implementation of the JAXBContext interface.

*

Responsibilities:

    *
  • Create Marshaller instances
  • *
  • Create Unmarshaller instances
  • *
  • Create Binder instances
  • *
  • Create Introspector instances
  • *
  • Create Validator instances
  • *
  • Generate Schema Files
  • *
*

This is the EclipseLink JAXB 2.0 implementation of jakarta.xml.bind.JAXBContext. This class * is created by the JAXBContextFactory and is used to create Marshallers, Unmarshallers, Validators, * Binders and Introspectors. A JAXBContext can also be used to create Schema Files.

*

Bootstrapping: * When bootstrapping the JAXBContext from a EclipseLink externalized metadata file(s) a number of * input options are available. The externalized metadata file (one per package) is passed in * through a property when creating the JAXBContext. The key used in the properties map is * "eclipselink-oxm-xml". The externalized metadata file can be set in the properties map in * one of three ways:

*

i) For a single externalized metadata file, one of the following can be set in the properties map:

    *
  • java.io.File
  • *
  • java.io.InputStream
  • *
  • java.io.Reader
  • *
  • java.net.URL
  • *
  • javax.xml.stream.XMLEventReader
  • *
  • javax.xml.stream.XMLStreamReader
  • *
  • javax.xml.transform.Source
  • *
  • org.w3c.dom.Node
  • *
  • org.xml.sax.InputSource
* When using one of the above options, the package name must be set via package-name attribute on the * xml-bindings element in the externalized metadata file. *

ii) For multiple externalized metadata files where the package name is specified within each externalized * metadata file, a List can be used. The entries in the List are to be one of the types listed in i) above. *

iii) For multiple externalized metadata files where the package name is not specified in each externalized * metadata file, a Map can be used. The key must be a String (package name) and each value in the Map * (externalized metadata file) is to be one of the types listed in i) above. *

Note that in each of the above cases the package name can be set via package-name attribute on the * xml-bindings element in the externalized metadata file. If set, any java-type names in the given metadata * file that do not contain the package name will have that package name prepended to it. Also note that a * List or Map can be used for a single externalized metadata file. *

* @see jakarta.xml.bind.JAXBContext * @see org.eclipse.persistence.jaxb.JAXBMarshaller * @see org.eclipse.persistence.jaxb.JAXBUnmarshaller * @see org.eclipse.persistence.jaxb.JAXBBinder * @see org.eclipse.persistence.jaxb.JAXBIntrospector * @see org.eclipse.persistence.jaxb.JAXBContextProperties * * @author mmacivor */ public class JAXBContext extends jakarta.xml.bind.JAXBContext { private static final Map PARSER_FEATURES = new HashMap<>(2); static { PARSER_FEATURES.put("http://apache.org/xml/features/validation/schema/normalized-value", false); PARSER_FEATURES.put("http://apache.org/xml/features/validation/schema/element-default", false); if (MOXySystemProperties.moxyLoggingLevel != null) { AbstractSessionLog.getLog().setLevel(LogLevel.toValue(MOXySystemProperties.moxyLoggingLevel).getId(), SessionLog.MOXY); } } private static final String RI_XML_ACCESSOR_FACTORY_SUPPORT = "org.glassfish.jaxb.XmlAccessorFactory"; /** * For JAXB 2 there is no explicitly defined default validation handler * and the default event handling only terminates the operation after * encountering a fatal error. */ protected static final ValidationEventHandler DEFAULT_VALIDATION_EVENT_HANDLER = new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { return event.getSeverity() < ValidationEvent.FATAL_ERROR; } }; private final AtomicBoolean hasLoggedValidatorInfo = new AtomicBoolean(); protected JAXBContextInput contextInput; protected volatile JAXBContextState contextState; private XMLInputFactory xmlInputFactory; private boolean initializedXMLInputFactory = false; private JAXBMarshaller jsonSchemaMarshaller; private BeanValidationHelper beanValidationHelper; private Boolean beanValidationPresent; protected JAXBContext() { super(); contextState = new JAXBContextState(); initBeanValidation(); } protected JAXBContext(JAXBContextInput contextInput) throws jakarta.xml.bind.JAXBException { this.contextInput = contextInput; this.contextState = contextInput.createContextState(); initBeanValidation(); } /** * Create a JAXBContext for a given XMLContext. The XMLContext contains the * metadata about the Object to XML mappings. */ public JAXBContext(XMLContext context) { contextState = new JAXBContextState(context); initBeanValidation(); } /** * Create a JAXBContext. The XMLContext contains the metadata about the * Object to XML mappings. */ public JAXBContext(XMLContext context, Generator generator, Type[] boundTypes) { contextState = new JAXBContextState(context, generator, boundTypes, null); initBeanValidation(); } /** * Create a JAXBContext. The XMLContext contains the metadata about the * Object to XML mappings. */ public JAXBContext(XMLContext context, Generator generator, TypeMappingInfo[] boundTypes) { contextState = new JAXBContextState(context, generator, boundTypes, null); initBeanValidation(); } /** * Initializes bean validation if jakarta.validation.api bundle is on the class path. */ private void initBeanValidation() { if (beanValidationPresent == null) { beanValidationPresent = BeanValidationChecker.isBeanValidationPresent(); } if (beanValidationPresent && beanValidationHelper == null) { synchronized (JAXBContext.class) { if (beanValidationHelper == null) { // Bean validation is optional beanValidationHelper = new BeanValidationHelper(); } } } } /** * Returns BeanValidationHelper. Can return null if bean validation jar is not on class path. */ public BeanValidationHelper getBeanValidationHelper() { return beanValidationHelper; } public XMLInputFactory getXMLInputFactory() { if (!initializedXMLInputFactory) { try { xmlInputFactory = XMLInputFactory.newInstance(); } catch (FactoryConfigurationError e) { } finally { initializedXMLInputFactory = true; } } return xmlInputFactory; } AtomicBoolean getHasLoggedValidatorInfo() { return hasLoggedValidatorInfo; } /** * This event is called when context creation is completed, * and provides a chance to deference anything that is no longer * needed (to reduce the memory footprint of this object). */ void postInitialize() { if (this.contextState.generator != null) { this.contextState.generator.postInitialize(); } } /** * ADVANCED: *

Refresh the underlying metadata based on the inputs that were * used to create the JAXBContext. This is particularly useful when using * the virtual property mappings. The refreshMetadata call could be made * in the following way:

*
org.eclipse.persistence.jaxb.JAXBHelper.getJAXBContext(aJAXBContext).refreshMetadata();
* Note: *
    *
  • As instances of Binder maintain a cache, calling refreshMetadata will * not affect instances of Binder. To get the new metadata you must create * a new instance of Binder after the refresh metadata call has been made.
  • *
*/ public void refreshMetadata() throws jakarta.xml.bind.JAXBException { JAXBContextState newState = newContextState(); if (newState != null) { contextState = newState; } } /** * INTERNAL: * Build a new JAXBContextState from the current JAXBContextInput. */ private JAXBContextState newContextState() throws jakarta.xml.bind.JAXBException { if (null == contextInput) { return null; } synchronized (this) { JAXBContextState newState = contextInput.createContextState(); XMLContext xmlContext = getXMLContext(); xmlContext.setXMLContextState(newState.getXMLContext().getXMLContextState()); newState.setXMLContext(xmlContext); newState.setTypeToTypeMappingInfo(contextState.getTypeToTypeMappingInfo()); return newState; } } /** * INTERNAL: * Indicates if this JAXBContext can have its metadata refreshed. */ boolean isRefreshable() { return false; /* if (this.contextInput.properties == null) { return true; } if (this.contextInput.properties.containsKey(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY)) { return false; } return true; */ } /** * Return the XMLContext associated with this JAXBContext. */ public XMLContext getXMLContext() { return contextState.getXMLContext(); } public void setXMLContext(XMLContext xmlContext) { contextState.setXMLContext(xmlContext); } /** * Generate a Schema for this JAXBContext * * @param outputResolver Class that decides where the schema file (of the given namespace URI) will be written */ @Override public void generateSchema(SchemaOutputResolver outputResolver) { if(outputResolver instanceof JsonSchemaOutputResolver) { generateJsonSchema(outputResolver, ((JsonSchemaOutputResolver)outputResolver).getRootClass()); } else { generateSchema(outputResolver, null); } } public void generateJsonSchema(SchemaOutputResolver outputResolver, Class rootClass) { JsonSchemaGenerator generator = new JsonSchemaGenerator(this, this.contextState.properties); JsonSchema schema = generator.generateSchema(rootClass); try { Marshaller m = getJsonSchemaMarshaller(); m.marshal(schema, outputResolver.createOutput(null, rootClass.getName() + ".json")); } catch (Exception ex) { throw org.eclipse.persistence.exceptions.JAXBException.exceptionDuringSchemaGeneration(ex); } } private Marshaller getJsonSchemaMarshaller() throws jakarta.xml.bind.JAXBException { if (this.jsonSchemaMarshaller == null) { JAXBContext ctx = (JAXBContext) JAXBContextFactory.createContext(new Class[] { JsonSchema.class }, null); this.jsonSchemaMarshaller = ctx.createMarshaller(); this.jsonSchemaMarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON); this.jsonSchemaMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); this.jsonSchemaMarshaller.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, true); } return this.jsonSchemaMarshaller; } /** * Generate a Schema for this JAXBContext * * @param outputResolver Class that decides where the schema file (of the given namespace URI) will be written * @param additonalGlobalElements Map of additional global elements to be added to the generated XSD. * Note that if any QName in this map conflicts with another global element (for example from a TypeMappingInfo object) * then the element generated from this map will be the one that is present in the XSD. */ public void generateSchema(SchemaOutputResolver outputResolver, Map additonalGlobalElements) { JAXBContextState currentJAXBContextState = contextState; if (isRefreshable()) { // Recreate context state, to rebuild Generator try { currentJAXBContextState = newContextState(); } catch (Exception e) { throw JAXBException.exceptionDuringSchemaGeneration(e); } } XMLContext xmlContext = currentJAXBContextState.getXMLContext(); Generator generator = currentJAXBContextState.getGenerator(); if (generator == null) { SchemaModelGenerator smGen = new SchemaModelGenerator(xmlContext.getOxmConversionManager()); smGen.generateSchemas(xmlContext.getDescriptors(), null, new JAXBSchemaOutputResolver(outputResolver), additonalGlobalElements); } else { generator.generateSchemaFiles(outputResolver, additonalGlobalElements); } } /** * Create a JAXBMarshaller. The JAXBMarshaller is used to convert Java objects * to XML. */ @Override public JAXBMarshaller createMarshaller() throws jakarta.xml.bind.JAXBException { return contextState.createMarshaller(this); } /** * Create a JAXBUnmarshaller. The JAXBUnmarshaller is used to convert XML into * Java objects. */ @Override public JAXBUnmarshaller createUnmarshaller() throws jakarta.xml.bind.JAXBException { return contextState.createUnmarshaller(this); } /** * Create a JAXBValidator. The JAXBValidator is used to validate Java objects against * an XSD. */ public JAXBValidator createValidator() { return new JAXBValidator(getXMLContext().createValidator()); } /** * Create a JAXBBinder. The JAXBBinder is used to preserve unmapped XML Data. */ @Override public JAXBBinder createBinder() { return contextState.createBinder(this); } /** * Create a JAXBBinder. The JAXBBinder is used to preserve unmapped XML Data. * * @param nodeClass The DOM Node class to use */ @Override public JAXBBinder createBinder(Class nodeClass) { if (nodeClass.getName().equals("org.w3c.dom.Node")) { return contextState.createBinder(this); } else { throw new UnsupportedOperationException(JAXBException.unsupportedNodeClass(nodeClass.getName())); } } /** * Creates a JAXBIntrospector object. The JAXBIntrospector allows the user to * access certain pieces of metadata about an instance of a JAXB bound class. */ @Override public JAXBIntrospector createJAXBIntrospector() { return new JAXBIntrospector(getXMLContext()); } /** * INTERNAL: * Set the map containing which QName corresponds to which generated class. */ public void setQNameToGeneratedClasses(HashMap> qNameToClass) { contextState.setQNameToGeneratedClasses(qNameToClass); } /** * INTERNAL: * Get the map containing which Class (by name) corresponds to which generated class. */ public Map> getClassToGeneratedClasses() { return contextState.getClassToGeneratedClasses(); } /** * INTERNAL: * Set the map containing which Class (by name) corresponds to which generated class. */ public void setClassToGeneratedClasses(HashMap> classToClass) { contextState.setClassToGeneratedClasses(classToClass); } /** * ADVANCED: * Adjust the OXM metadata to take into account ORM mapping metadata */ public void applyORMMetadata(AbstractSession ormSession) { getXMLContext().applyORMMetadata(ormSession); } /** * INTERNAL: * Get the map of which QName corresponds to which declared class. */ public Map> getQNamesToDeclaredClasses() { return contextState.getQNamesToDeclaredClasses(); } /** * INTERNAL: * Get the map of which QName corresponds to which generated class. */ Map> getQNameToGeneratedClasses() { return contextState.getQNameToGeneratedClasses(); } /** * INTERNAL: * Set the map of which QName corresponds to which declared class. */ public void setQNamesToDeclaredClasses(HashMap> nameToDeclaredClasses) { contextState.setQNamesToDeclaredClasses(nameToDeclaredClasses); } /** * INTERNAL: * Get the map for which array class (by name) corresponds to which generated class */ public Map> getArrayClassesToGeneratedClasses() { if (contextState.getGenerator() == null) { return null; } return contextState.getGenerator().getAnnotationsProcessor().getArrayClassesToGeneratedClasses(); } /** * INTERNAL: * Get the map for which collection class (by Type) corresponds to which generated class */ public Map> getCollectionClassesToGeneratedClasses() { if (contextState.getGenerator() == null) { return null; } return contextState.getGenerator().getAnnotationsProcessor().getCollectionClassesToGeneratedClasses(); } /** * INTERNAL: * Populate the map of which Type corresponds to which QName. * The keys should be all the boundTypes used to create the JAXBContext. * If the JAXBContext was not created with the constructor that takes a Type[] then * this Map will be empty. */ public void initTypeToSchemaType() { contextState.initTypeToSchemaType(); } /** * INTERNAL: * Get the map of which TypeMappingInfo corresponds to which QName. * The keys should be all the boundTypes used to create the JAXBContext. * If the JAXBContext was not created with the constructor that takes a TypeMappingInfo[] * this Map will be empty. */ public Map getTypeMappingInfoToSchemaType() { return contextState.getTypeMappingInfoToSchemaType(); } /** * INTERNAL: * Get the map of which Type corresponds to which QName. * The keys should be all the boundTypes used to create the JAXBContext. * If the JAXBContext was not created with the constructor that takes a Type[] then * this Map will be empty. */ public Map getTypeToSchemaType() { return contextState.getTypeToSchemaType(); } Map> getTypeMappingInfoToGeneratedType() { return contextState.getTypeMappingInfoToGeneratedType(); } Map getTypeToTypeMappingInfo() { return contextState.getTypeToTypeMappingInfo(); } void setTypeToTypeMappingInfo(Map typeToMappingInfo) { contextState.setTypeToTypeMappingInfo(typeToMappingInfo); } void setTypeMappingInfoToJavaTypeAdapaters(Map typeMappingInfoToAdapters) { contextState.setTypeMappingInfoToJavaTypeAdapaters(typeMappingInfoToAdapters); } static class RootLevelXmlAdapter { private XmlAdapter xmlAdapter; private Class boundType; public RootLevelXmlAdapter(XmlAdapter adapter, Class boundType) { this.xmlAdapter = adapter; this.boundType = boundType; } public XmlAdapter getXmlAdapter() { return xmlAdapter; } public Class getBoundType() { return boundType; } public void setXmlAdapter(XmlAdapter xmlAdapter) { this.xmlAdapter = xmlAdapter; } public void setBoundType(Class boundType) { this.boundType = boundType; } } Map getTypeMappingInfoToJavaTypeAdapters() { return contextState.getTypeMappingInfoToJavaTypeAdapters(); } /** * Get a value from an object based on an XPath statement. * * @param * The return type of this method corresponds to the returnType parameter. * @param object * The XPath will be executed relative to this object. * @param xPath * The XPath statement. * @param namespaceResolver * A NamespaceResolver containing the prefix/URI pairings from the XPath statement. * @param returnType * The return type. * * @return * The object corresponding to the XPath or null if no result was found. */ public T getValueByXPath(Object object, String xPath, NamespaceResolver namespaceResolver, Class returnType) { return getXMLContext().getValueByXPath(object, xPath, namespaceResolver, returnType); } /** * Set a value on an object based on an XPath statement. * * @param object * The XPath will be executed relative to this object. * @param xPath * The XPath statement. * @param namespaceResolver * A NamespaceResolver containing the prefix/URI pairings from the XPath statement. * @param value * The value to be set. */ public void setValueByXPath(Object object, String xPath, NamespaceResolver namespaceResolver, Object value) { getXMLContext().setValueByXPath(object, xPath, namespaceResolver, value); } /** * Create a new object instance for a given XML namespace and name. * * @param namespace * The namespace of the complex type to create a new Java instance of. * @param typeName * The XML type name to create a new Java instance of. * @param isGlobalType * True if the object to be created represents a global type, false if it * represents a global element. * * @return * An instance of the Java class mapped to the indicated XML type, or null * if no result was found. */ public Object createByQualifiedName(String namespace, String typeName, boolean isGlobalType) { return getXMLContext().createByQualifiedName(namespace, typeName, isGlobalType); } /** * Create a new object instance for a given XPath, relative to the parentObject. * * @param * The return type of this method corresponds to the returnType parameter. * @param parentObject * The XPath will be executed relative to this object. * @param xPath * The XPath statement. * @param namespaceResolver * A NamespaceResolver containing the prefix/URI pairings from the XPath statement. * @param returnType * The return type. * * @return * An instance of the Java class mapped to the supplied XML type, or null * if no result was found. */ public T createByXPath(Object parentObject, String xPath, NamespaceResolver namespaceResolver, Class returnType) { return getXMLContext().createByXPath(parentObject, xPath, namespaceResolver, returnType); } public ObjectGraph createObjectGraph(Class type) { CoreAttributeGroup group = new CoreAttributeGroup(null, type, true); return new ObjectGraphImpl(group); } public ObjectGraph createObjectGraph(String typeName) { ClassLoader loader = this.contextInput.classLoader; try { Class cls = PrivilegedAccessHelper.getClassForName(typeName, true, loader); return createObjectGraph(cls); } catch (Exception ex) { throw ConversionException.couldNotBeConvertedToClass(typeName, Class.class, ex); } } protected JAXBElement createJAXBElementFromXMLRoot(Root xmlRoot, Class declaredType) { Object value = xmlRoot.getObject(); if (value instanceof List theList) { for (int i = 0; i < theList.size(); i++) { Object next = theList.get(i); if (next instanceof Root) { theList.set(i, createJAXBElementFromXMLRoot((Root) next, declaredType)); } } } else if (value instanceof WrappedValue) { QName qname = new QName(xmlRoot.getNamespaceURI(), xmlRoot.getLocalName()); return new JAXBElement(qname, ((WrappedValue) value).getDeclaredType(), ((WrappedValue) value).getValue()); } else if (value instanceof JAXBElement) { return (JAXBElement) value; } else if (value instanceof ManyValue) { value = ((ManyValue) value).getItem(); } QName qname = new QName(xmlRoot.getNamespaceURI(), xmlRoot.getLocalName()); Map> qNamesToDeclaredClasses = getQNamesToDeclaredClasses(); if (qNamesToDeclaredClasses != null && !qNamesToDeclaredClasses.isEmpty()) { Class declaredClass = qNamesToDeclaredClasses.get(qname); if (declaredClass != null) { return createJAXBElement(qname, declaredClass, value); } } Class xmlRootDeclaredType = xmlRoot.getDeclaredType(); if (xmlRootDeclaredType != null) { return createJAXBElement(qname, xmlRootDeclaredType, value); } return createJAXBElement(qname, declaredType, value); } protected JAXBElement createJAXBElement(QName qname, Class theClass, Object value) { if (theClass == null) { return new JAXBElement(qname, Object.class, value); } if (CoreClassConstants.XML_GREGORIAN_CALENDAR.isAssignableFrom(theClass)) { theClass = CoreClassConstants.XML_GREGORIAN_CALENDAR; } else if (CoreClassConstants.DURATION.isAssignableFrom(theClass)) { theClass = CoreClassConstants.DURATION; } return new JAXBElement(qname, theClass, value); } /** * Returns true if any Object in this context contains a property annotated with an XmlAttachmentRef * annotation. */ public boolean hasSwaRef() { return contextState.getGenerator().getAnnotationsProcessor().hasSwaRef(); } /** * The JAXBContextInput is used to create a JAXBContextState which is responsible for accessing * the underlying XMLContext */ public static abstract class JAXBContextInput { protected Map properties; protected ClassLoader classLoader; /** * Create a new JAXBContextInput with the specified Map of properties and ClassLoader. * @param properties Map of properties. * @param classLoader the classLoader to use. If null then Thread.currentThread().getContextClassLoader() will be used. */ public JAXBContextInput(Map properties, ClassLoader classLoader) { SessionLog logger = AbstractSessionLog.getLog(); if (properties != null && logger.shouldLog(SessionLog.FINE, SessionLog.MOXY)) { for (Map.Entry item : (Set>)(properties.entrySet())) { if (item.getValue() == null) { logger.log(SessionLog.FINE, SessionLog.MOXY, "moxy_set_jaxb_context_property", new Object[]{item.getKey(), "NULL"}); } else { logger.log(SessionLog.FINE, SessionLog.MOXY, "moxy_set_jaxb_context_property", new Object[]{item.getKey(), item.getValue()}); } } } this.properties = properties; if (null == classLoader) { this.classLoader = Thread.currentThread().getContextClassLoader(); } else { this.classLoader = classLoader; } } protected abstract JAXBContextState createContextState() throws jakarta.xml.bind.JAXBException; protected Collection sessionEventListeners() { Object eventListenerFromProperties = null; if (this.properties != null) { eventListenerFromProperties = properties.get(JAXBContextProperties.SESSION_EVENT_LISTENER); } List eventListeners = null; if (null == eventListenerFromProperties) { eventListeners = new ArrayList<>(1); } else { if (eventListenerFromProperties instanceof SessionEventListener) { eventListeners = new ArrayList<>(2); eventListeners.add((SessionEventListener) eventListenerFromProperties); } else if (eventListenerFromProperties instanceof Collection) { List listeners = (List) eventListenerFromProperties; eventListeners = new ArrayList<>(listeners.size() + 1); eventListeners.addAll(listeners); } } // disable instantiation policy validation during descriptor initialization org.eclipse.persistence.internal.jaxb.SessionEventListener eventListener = new org.eclipse.persistence.internal.jaxb.SessionEventListener(); eventListener.setShouldValidateInstantiationPolicy(false); eventListeners.add(eventListener); return eventListeners; } } static class ContextPathInput extends JAXBContextInput { private String contextPath; ContextPathInput(String contextPath, Map properties, ClassLoader classLoader) { super(properties, classLoader); this.contextPath = contextPath; } @Override protected JAXBContextState createContextState() throws jakarta.xml.bind.JAXBException { boolean foundMetadata = false; List> classes = new ArrayList<>(); // Check properties map for eclipselink-oxm.xml entries Map xmlBindingMap = JAXBContextFactory.getXmlBindingsFromProperties(properties, classLoader); foundMetadata = null != xmlBindingMap && !xmlBindingMap.isEmpty(); classes = getXmlBindingsClassesFromMap(xmlBindingMap, classLoader, classes); StringTokenizer tokenizer = new StringTokenizer(contextPath, ":"); while (tokenizer.hasMoreElements()) { String path = tokenizer.nextToken(); try { Class objectFactory = classLoader.loadClass(path + ".ObjectFactory"); if (isJAXB2ObjectFactory(objectFactory, classLoader)) { classes.add(objectFactory); foundMetadata = true; } } catch (Exception ex) { // if there's no object factory, don't worry about it. Check for jaxb.index next } try { // try to load package info just to be safe classLoader.loadClass(path + ".package-info"); } catch (Exception ex) { } // Next check for a jaxb.index file in case there's one available InputStream jaxbIndex = classLoader.getResourceAsStream(path.replace('.', '/') + "/jaxb.index"); if (jaxbIndex != null) { foundMetadata = true; BufferedReader reader = new BufferedReader(new InputStreamReader(jaxbIndex)); try { String line = reader.readLine(); while (line != null) { String className = path + JAXBContextFactory.PKG_SEPARATOR + line.trim(); try { classes.add(classLoader.loadClass(className)); } catch (Exception ex) { // just ignore for now if the class isn't available. } line = reader.readLine(); } } catch (Exception ex) { } finally { try { reader.close(); } catch (Exception e) { // ignore } } } } if (foundMetadata) { Class[] classArray = new Class[classes.size()]; for (int i = 0; i < classes.size(); i++) { classArray[i] = classes.get(i); } openToCore(classes); return createContextState(classArray, xmlBindingMap); } Exception sessionLoadingException = null; try { openToCore(classes); XMLContext xmlContext = new XMLContext(contextPath, classLoader); return new JAXBContextState(xmlContext); } catch (Exception exception) { sessionLoadingException = exception; } JAXBException jaxbException = JAXBException.noObjectFactoryOrJaxbIndexInPath(contextPath); if (sessionLoadingException != null) { jaxbException.setInternalException(sessionLoadingException); } throw new jakarta.xml.bind.JAXBException(jaxbException); } /** * This means of creating a JAXBContext is aimed at creating a JAXBContext * based on method parameters. This method is useful when JAXB is used as * the binding layer for a Web Service provider. */ private JAXBContextState createContextState(Class[] classesToBeBound, Map xmlBindings) throws jakarta.xml.bind.JAXBException { JaxbClassLoader loader = PrivilegedAccessHelper.callDoPrivileged( () -> new JaxbClassLoader(classLoader, classesToBeBound) ); String defaultTargetNamespace = null; AnnotationHelper annotationHelper = null; boolean enableXmlAccessorFactory = false; if (properties != null) { if ((defaultTargetNamespace = (String) properties.get(JAXBContextProperties.DEFAULT_TARGET_NAMESPACE)) == null) { // try looking up the 'old' key defaultTargetNamespace = (String) properties.get(JAXBContextFactory.DEFAULT_TARGET_NAMESPACE_KEY); } if ((annotationHelper = (AnnotationHelper) properties.get(JAXBContextProperties.ANNOTATION_HELPER)) == null) { // try looking up the 'old' key annotationHelper = (AnnotationHelper) properties.get(JAXBContextFactory.ANNOTATION_HELPER_KEY); } Boolean xmlAccessorFactorySupport = (Boolean) properties.get(JAXBContextProperties.XML_ACCESSOR_FACTORY_SUPPORT); Boolean xmlAccessorFactorySupportRI = (Boolean) properties.get(RI_XML_ACCESSOR_FACTORY_SUPPORT); if (Boolean.TRUE.equals(xmlAccessorFactorySupport) || Boolean.TRUE.equals(xmlAccessorFactorySupportRI)) { enableXmlAccessorFactory = true; } } JavaModelImpl jModel; if (annotationHelper != null) { jModel = new JavaModelImpl(loader, annotationHelper); } else { jModel = new JavaModelImpl(loader); } // create Map of package names to metadata complete indicators Map metadataComplete = new HashMap<>(); for (String packageName : xmlBindings.keySet()) { if (xmlBindings.get(packageName).isXmlMappingMetadataComplete()) { metadataComplete.put(packageName, true); } } if (!metadataComplete.isEmpty()) { jModel.setMetadataCompletePackageMap(metadataComplete); } jModel.setHasXmlBindings(!xmlBindings.isEmpty()); JavaModelInputImpl inputImpl = new JavaModelInputImpl(classesToBeBound, jModel); if (properties != null) enableFacetsIfPropertySetTrue(inputImpl, properties); try { Generator generator = new Generator(inputImpl, xmlBindings, loader, defaultTargetNamespace, enableXmlAccessorFactory); return createContextState(generator, loader, classesToBeBound, properties); } catch (Exception ex) { throw new jakarta.xml.bind.JAXBException(ex.getMessage(), ex); } } private JAXBContextState createContextState(Generator generator, JaxbClassLoader loader, Type[] typesToBeBound, Map properties) throws Exception { CoreProject proj = generator.generateProject(); ConversionManager conversionManager = null; if (classLoader != null) { conversionManager = new ConversionManager(); conversionManager.setLoader(loader); } else { conversionManager = ConversionManager.getDefaultManager(); } proj.convertClassNamesToClasses(conversionManager.getLoader()); // need to make sure that the java class is set properly on each // descriptor when using java classname - req'd for JOT api implementation for (Iterator descriptorIt = proj.getOrderedDescriptors().iterator(); descriptorIt.hasNext();) { ClassDescriptor descriptor = descriptorIt.next(); if (descriptor.getJavaClass() == null) { descriptor.setJavaClass(conversionManager.convertClassNameToClass(descriptor.getJavaClassName())); } } XMLPlatform platform = new SAXPlatform(); platform.getConversionManager().setLoader(loader); XMLContext xmlContext = new XMLContext((Project) proj, loader, sessionEventListeners()); ((XMLLogin) xmlContext.getSession().getDatasourceLogin()).setEqualNamespaceResolvers(true); return new JAXBContextState(xmlContext, generator, typesToBeBound, properties); } /** * Convenience method that returns an array of Classes based on a map given XmlBindings and an * array of existing classes. The resulting array will not contain duplicate entries. */ private List> getXmlBindingsClassesFromMap(Map xmlBindingMap, ClassLoader classLoader, List> existingClasses) { List> additionalClasses = existingClasses; // for each xmlBindings for (Entry entry : xmlBindingMap.entrySet()) { additionalClasses = getXmlBindingsClasses(entry.getValue(), classLoader, additionalClasses); } return additionalClasses; } /** * Convenience method that returns a list of Classes based on a given XmlBindings and an array * of existing classes. The resulting array will not contain duplicate entries. */ private List> getXmlBindingsClasses(XmlBindings xmlBindings, ClassLoader classLoader, List> existingClasses) { List> additionalClasses = existingClasses; JavaTypes jTypes = xmlBindings.getJavaTypes(); if (jTypes != null) { for (JavaType javaType : jTypes.getJavaType()) { try { Class jClass = classLoader.loadClass(getQualifiedJavaTypeName(javaType.getName(), xmlBindings.getPackageName())); if (!additionalClasses.contains(jClass)) { additionalClasses.add(jClass); } } catch (ClassNotFoundException e) { throw org.eclipse.persistence.exceptions.JAXBException.couldNotLoadClassFromMetadata(javaType.getName()); } } } return additionalClasses; } private boolean isJAXB2ObjectFactory(Class objectFactoryClass, ClassLoader classLoader) { try { Class xmlRegistry = PrivilegedAccessHelper.getClassForName("jakarta.xml.bind.annotation.XmlRegistry", false, classLoader); if (objectFactoryClass.isAnnotationPresent(xmlRegistry)) { return true; } return false; } catch (Exception ex) { return false; } } } static class TypeMappingInfoInput extends JAXBContextInput { private TypeMappingInfo[] typeMappingInfo; TypeMappingInfoInput(TypeMappingInfo[] typeMappingInfo, Map properties, ClassLoader classLoader) { super(properties, classLoader); this.typeMappingInfo = Arrays.copyOf(typeMappingInfo, typeMappingInfo.length); Arrays.sort(this.typeMappingInfo, new Comparator<>() { @Override public int compare(TypeMappingInfo javaClass1, TypeMappingInfo javaClass2) { String sourceName = getNameForType(javaClass1.getType()); String targetName = getNameForType(javaClass2.getType()); if (sourceName == null || targetName == null) { return -1; } return sourceName.compareTo(targetName); } private String getNameForType(Type type) { if (type instanceof Class) { return ((Class) type).getCanonicalName(); } else if (type instanceof GenericArrayType) { Class genericTypeClass = (Class) ((GenericArrayType) type).getGenericComponentType(); return genericTypeClass.getCanonicalName(); } else { // assume parameterized type ParameterizedType pType = (ParameterizedType) type; return ((Class) pType.getRawType()).getCanonicalName(); } } }); } @Override protected JAXBContextState createContextState() throws jakarta.xml.bind.JAXBException { // Check properties map for eclipselink-oxm.xml entries Map xmlBindings = JAXBContextFactory.getXmlBindingsFromProperties(properties, classLoader); String defaultTargetNamespace = null; AnnotationHelper annotationHelper = null; boolean enableXmlAccessorFactory = false; if (properties != null) { if ((defaultTargetNamespace = (String) properties.get(JAXBContextProperties.DEFAULT_TARGET_NAMESPACE)) == null) { // try looking up the 'old' key defaultTargetNamespace = (String) properties.get(JAXBContextFactory.DEFAULT_TARGET_NAMESPACE_KEY); } if ((annotationHelper = (AnnotationHelper) properties.get(JAXBContextProperties.ANNOTATION_HELPER)) == null) { // try looking up the 'old' key annotationHelper = (AnnotationHelper) properties.get(JAXBContextFactory.ANNOTATION_HELPER_KEY); } Boolean xmlAccessorFactorySupport = (Boolean) properties.get(JAXBContextProperties.XML_ACCESSOR_FACTORY_SUPPORT); Boolean xmlAccessorFactorySupportRI = (Boolean) properties.get(RI_XML_ACCESSOR_FACTORY_SUPPORT); if (Boolean.TRUE.equals(xmlAccessorFactorySupport) || Boolean.TRUE.equals(xmlAccessorFactorySupportRI)) { enableXmlAccessorFactory = true; } } TypeMappingInfo[] typesToBeBound = typeMappingInfo; for (Entry entry : xmlBindings.entrySet()) { typesToBeBound = getXmlBindingsClasses(entry.getValue(), classLoader, typesToBeBound); } final TypeMappingInfo[] types = typesToBeBound; JaxbClassLoader loader = PrivilegedAccessHelper.callDoPrivileged( () -> new JaxbClassLoader(classLoader, types) ); JavaModelImpl jModel; if (annotationHelper != null) { jModel = new JavaModelImpl(loader, annotationHelper); } else { jModel = new JavaModelImpl(loader); } if (xmlBindings != null) { jModel.setHasXmlBindings(!xmlBindings.isEmpty()); // create Map of package names to metadata complete indicators Map metadataComplete = new HashMap<>(); for (String packageName : xmlBindings.keySet()) { if (xmlBindings.get(packageName).isXmlMappingMetadataComplete()) { metadataComplete.put(packageName, true); } } if (!metadataComplete.isEmpty()) { jModel.setMetadataCompletePackageMap(metadataComplete); } } JavaModelInputImpl inputImpl = new JavaModelInputImpl(typesToBeBound, jModel); if (properties != null) enableFacetsIfPropertySetTrue(inputImpl, properties); try { openToCore(inputImpl); Generator generator = new Generator(inputImpl, typesToBeBound, inputImpl.getJavaClasses(), null, xmlBindings, classLoader, defaultTargetNamespace, enableXmlAccessorFactory); JAXBContextState contextState = createContextState(generator, loader, typesToBeBound, properties); return contextState; } catch (Exception ex) { throw new jakarta.xml.bind.JAXBException(ex.getMessage(), ex); } } private JAXBContextState createContextState(Generator generator, JaxbClassLoader loader, TypeMappingInfo[] typesToBeBound, Map properties) throws Exception { CoreProject proj = generator.generateProject(); ConversionManager conversionManager = null; if (classLoader != null) { conversionManager = new ConversionManager(); conversionManager.setLoader(loader); } else { conversionManager = ConversionManager.getDefaultManager(); } proj.convertClassNamesToClasses(conversionManager.getLoader()); // need to make sure that the java class is set properly on each // descriptor when using java classname - req'd for JOT api implementation for (ClassDescriptor descriptor : (Iterable) proj.getOrderedDescriptors()) { if (descriptor.getJavaClass() == null) { descriptor.setJavaClass(conversionManager.convertClassNameToClass(descriptor.getJavaClassName())); } } XMLPlatform platform = new SAXPlatform(); platform.getConversionManager().setLoader(loader); XMLContext xmlContext = new XMLContext((Project) proj, loader, sessionEventListeners()); ((XMLLogin) xmlContext.getSession().getDatasourceLogin()).setEqualNamespaceResolvers(true); JAXBContextState contextState = new JAXBContextState(xmlContext, generator, typesToBeBound, properties); for (TypeMappingInfo typeMappingInfo : typesToBeBound) { Type classToLookup = typeMappingInfo.getType(); if (contextState.getTypeMappingInfoToGeneratedType() != null && !contextState.getTypeMappingInfoToGeneratedType().isEmpty()) { Class generatedClass = contextState.getTypeMappingInfoToGeneratedType().get(typeMappingInfo); if (generatedClass != null) { classToLookup = generatedClass; } } if (classToLookup != null && classToLookup.getClass() == Class.class) { Descriptor xmlDescriptor = (Descriptor) proj.getDescriptor((Class) classToLookup); typeMappingInfo.setXmlDescriptor(xmlDescriptor); } } return contextState; } /** * Convenience method that returns an array of Types based on a given XmlBindings. The resulting * array will not contain duplicate entries. */ private static TypeMappingInfo[] getXmlBindingsClasses(XmlBindings xmlBindings, ClassLoader classLoader, TypeMappingInfo[] existingTypes) { JavaTypes jTypes = xmlBindings.getJavaTypes(); if (jTypes != null) { List> existingClasses = new ArrayList<>(existingTypes.length); for (TypeMappingInfo typeMappingInfo : existingTypes) { Type type = typeMappingInfo.getType(); if (type == null) { throw org.eclipse.persistence.exceptions.JAXBException.nullTypeOnTypeMappingInfo(typeMappingInfo.getXmlTagName()); } // ignore ParameterizedTypes if (type instanceof Class) { Class cls = (Class) type; existingClasses.add(cls); } } List additionalTypeMappingInfos = new ArrayList<>(jTypes.getJavaType().size()); for (JavaType javaType : jTypes.getJavaType()) { try { Class nextClass = classLoader.loadClass(getQualifiedJavaTypeName(javaType.getName(), xmlBindings.getPackageName())); if (!(existingClasses.contains(nextClass))) { TypeMappingInfo typeMappingInfo = new TypeMappingInfo(); typeMappingInfo.setType(nextClass); additionalTypeMappingInfos.add(typeMappingInfo); existingClasses.add(nextClass); } } catch (ClassNotFoundException e) { throw org.eclipse.persistence.exceptions.JAXBException.couldNotLoadClassFromMetadata(javaType.getName()); } } TypeMappingInfo[] allTypeMappingInfos = new TypeMappingInfo[existingTypes.length + additionalTypeMappingInfos.size()]; System.arraycopy(existingTypes, 0, allTypeMappingInfos, 0, existingTypes.length); Object[] additionalTypes = additionalTypeMappingInfos.toArray(); System.arraycopy(additionalTypes, 0, allTypeMappingInfos, existingTypes.length, additionalTypes.length); return allTypeMappingInfos; } return existingTypes; } } protected static class JAXBContextState { private XMLContext xmlContext; private org.eclipse.persistence.jaxb.compiler.Generator generator; private Map> qNameToGeneratedClasses; private Map> classToGeneratedClasses; private Map> qNamesToDeclaredClasses; private Map typeToSchemaType; private TypeMappingInfo[] boundTypes; private Map> typeMappingInfoToGeneratedType; private Map typeToTypeMappingInfo; private Map typeMappingInfoToJavaTypeAdapters; private Map properties; protected JAXBContextState() { } protected JAXBContextState(XMLContext context) { xmlContext = context; updateNamespaces(); } protected JAXBContextState(XMLContext context, Generator generator, Type[] boundTypes, Map properties) { this(context); this.generator = generator; this.qNameToGeneratedClasses = generator.getMappingsGenerator().getQNamesToGeneratedClasses(); this.classToGeneratedClasses = generator.getMappingsGenerator().getClassToGeneratedClasses(); this.qNamesToDeclaredClasses = generator.getMappingsGenerator().getQNamesToDeclaredClasses(); this.boundTypes = new TypeMappingInfo[boundTypes.length]; for (int i = 0; i < boundTypes.length; i++) { TypeMappingInfo newTypeInfo = new TypeMappingInfo(); newTypeInfo.setType(boundTypes[i]); this.boundTypes[i] = newTypeInfo; } if (properties != null) { this.properties = new HashMap(properties); } } protected JAXBContextState(XMLContext context, Generator generator, TypeMappingInfo[] boundTypes, Map properties) { this(context); this.generator = generator; this.qNameToGeneratedClasses = generator.getMappingsGenerator().getQNamesToGeneratedClasses(); this.classToGeneratedClasses = generator.getMappingsGenerator().getClassToGeneratedClasses(); this.qNamesToDeclaredClasses = generator.getMappingsGenerator().getQNamesToDeclaredClasses(); this.typeMappingInfoToGeneratedType = generator.getAnnotationsProcessor().getTypeMappingInfosToGeneratedClasses(); this.setTypeMappingInfoToJavaTypeAdapaters(createAdaptersForAdapterClasses(generator.getAnnotationsProcessor().getTypeMappingInfoToAdapterClasses())); this.boundTypes = boundTypes; if (properties != null) { this.properties = new HashMap(properties); } } private Map createAdaptersForAdapterClasses(Map> typeMappingInfoToAdapterClasses) { Map typeMappingInfoToAdapters = new HashMap<>(); for (Entry> entry : typeMappingInfoToAdapterClasses.entrySet()) { Class adapterClass = entry.getValue(); if (adapterClass != null) { try { XmlAdapter adapter = (XmlAdapter) adapterClass.getConstructor().newInstance(); Class boundType = getBoundTypeForXmlAdapterClass(adapterClass); RootLevelXmlAdapter rootLevelXmlAdapter = new RootLevelXmlAdapter(adapter, boundType); typeMappingInfoToAdapters.put(entry.getKey(), rootLevelXmlAdapter); } catch (Exception ex) { } } } return typeMappingInfoToAdapters; } private Class getBoundTypeForXmlAdapterClass(Class adapterClass) { Class boundType = Object.class; for (Method method : PrivilegedAccessHelper.getDeclaredMethods(adapterClass)) { if (method.getName().equals("marshal")) { Class returnType = PrivilegedAccessHelper.getMethodReturnType(method); if (!returnType.getName().equals(boundType.getName())) { boundType = returnType; break; } } } return boundType; } private void updateNamespaces() { Collection descriptors = xmlContext.getSession().getDescriptors().values(); for (Object descriptor : descriptors) { Descriptor desc = (Descriptor) descriptor; processXMLDescriptor(new ArrayList<>(), desc, desc.getNonNullNamespaceResolver()); } } private void processRefClasses(List processed, Set refClasses, org.eclipse.persistence.internal.oxm.NamespaceResolver nr) { if (refClasses != null) { for (Object refClass : refClasses) { Class nextClass = (Class) refClass; Descriptor desc = (Descriptor) xmlContext.getSession().getProject().getDescriptor(nextClass); processXMLDescriptor(processed, desc, nr); } } } private void processXMLDescriptor(List processed, Descriptor desc, org.eclipse.persistence.internal.oxm.NamespaceResolver nr) { if (desc == null || processed.contains(desc)) { return; } processed.add(desc); List mappings = desc.getMappings(); for (Object mapping : mappings) { DatabaseMapping nextMapping = (DatabaseMapping) mapping; List fields = nextMapping.getFields(); updateResolverForFields(fields, nr); Descriptor refDesc = (Descriptor) nextMapping.getReferenceDescriptor(); if (refDesc != null && !processed.contains(refDesc)) { processXMLDescriptor(processed, refDesc, nr); } if (nextMapping instanceof ChoiceObjectMapping) { Set refClasses = ((ChoiceObjectMapping) nextMapping).getClassToFieldMappings().keySet(); processRefClasses(processed, refClasses, nr); } else if (nextMapping instanceof ChoiceCollectionMapping) { Set refClasses = ((ChoiceCollectionMapping) nextMapping).getClassToFieldMappings().keySet(); processRefClasses(processed, refClasses, nr); } } } private void updateResolverForFields(Collection fields, org.eclipse.persistence.internal.oxm.NamespaceResolver nr) { for (Object field1 : fields) { Field field = (XMLField) field1; XPathFragment currentFragment = field.getXPathFragment(); while (currentFragment != null) { String uri = currentFragment.getNamespaceURI(); if (uri != null) { String prefix = currentFragment.getPrefix(); if (prefix == null || prefix.isEmpty()) { if (null == nr.getDefaultNamespaceURI()) { nr.setDefaultNamespaceURI(uri); } } else { if (null == nr.resolveNamespacePrefix(prefix)) { nr.put(prefix, uri); } } } currentFragment = currentFragment.getNextFragment(); } } } private Map> getClassToGeneratedClasses() { return classToGeneratedClasses; } private Generator getGenerator() { return generator; } private XMLContext getXMLContext() { return this.xmlContext; } private Map getTypeToSchemaType() { if (typeToSchemaType == null) { initTypeToSchemaType(); } return typeToSchemaType; } private Map> getTypeMappingInfoToGeneratedType() { return this.typeMappingInfoToGeneratedType; } private Map getTypeMappingInfoToJavaTypeAdapters() { return this.typeMappingInfoToJavaTypeAdapters; } private Map getTypeToTypeMappingInfo() { return this.typeToTypeMappingInfo; } private Map getTypeMappingInfoToSchemaType() { if (typeToTypeMappingInfo != null && !typeToTypeMappingInfo.isEmpty()) { return new HashMap<>(); } return generator.getAnnotationsProcessor().getTypeMappingInfosToSchemaTypes(); } private Map> getQNamesToDeclaredClasses() { return qNamesToDeclaredClasses; } /** * INTERNAL: * Get the QName which the given Type corresponds to. * Valid types should be all the boundTypes used to create the JAXBContext. * If the JAXBContext was not created with the construction that takes a Type[] then * this will be return null. */ private QName getSchemaTypeForTypeMappingInfo(Type type) { QName name = null; //Check for annotation overrides if (type instanceof Class) { name = generator.getAnnotationsProcessor().getUserDefinedSchemaTypes().get(((Class) type).getName()); if (name == null) { Class theClass = (Class) type; //Change default for byte[] to Base64 (JAXB 2.0 default) if (type == CoreClassConstants.ABYTE || type == CoreClassConstants.APBYTE || theClass.getCanonicalName().equals("java.awt.Image") || type == Source.class || theClass.getCanonicalName().equals("jakarta.activation.DataHandler")) { name = Constants.BASE_64_BINARY_QNAME; } else if (type == CoreClassConstants.OBJECT) { name = Constants.ANY_TYPE_QNAME; } else if (type == CoreClassConstants.XML_GREGORIAN_CALENDAR) { name = Constants.ANY_SIMPLE_TYPE_QNAME; } else { name = XMLConversionManager.getDefaultJavaTypes().get(type); } } } return name; } private Map> getQNameToGeneratedClasses() { return qNameToGeneratedClasses; } private void initTypeToSchemaType() { this.typeToSchemaType = new HashMap<>(); if (typeToTypeMappingInfo == null || typeToTypeMappingInfo.isEmpty()) { return; } //Add schema types generated for mapped domain classes for (Object o : xmlContext.getSession().getProject().getOrderedDescriptors()) { Descriptor next = (Descriptor) o; Class javaClass = next.getJavaClass(); if (next.getSchemaReference() != null) { QName schemaType = next.getSchemaReference().getSchemaContextAsQName(next.getNamespaceResolver()); Type type = null; if (generator != null) { type = generator.getAnnotationsProcessor().getGeneratedClassesToCollectionClasses().get(javaClass); if (type == null) { JavaClass arrayClass = generator.getAnnotationsProcessor().getGeneratedClassesToArrayClasses().get(javaClass); if (arrayClass != null) { String arrayClassName = arrayClass.getName(); try { type = PrivilegedAccessHelper.getClassForName(arrayClassName); } catch (Exception ex) { } } if (type == null && getTypeMappingInfoToGeneratedType() != null) { for (Entry> entry : getTypeMappingInfoToGeneratedType().entrySet()) { if (entry.getValue().equals(javaClass)) { type = entry.getKey().getType(); break; } } } } if (type == null) { type = javaClass; } } else { type = javaClass; } this.typeToSchemaType.put(type, schemaType); } } //Add any types that we didn't generate descriptors for (built in types) if (boundTypes != null) { for (TypeMappingInfo next : this.boundTypes) { Type nextType = next.getType(); if (this.typeToSchemaType.get(nextType) == null) { QName name = getSchemaTypeForTypeMappingInfo(nextType); if (name != null) { this.typeToSchemaType.put(nextType, name); } } } } } private void setClassToGeneratedClasses(HashMap> classToClass) { this.classToGeneratedClasses = classToClass; } private void setTypeToTypeMappingInfo(Map typeToMappingInfo) { this.typeToTypeMappingInfo = typeToMappingInfo; if (this.generator != null) { this.generator.setTypeToTypeMappingInfo(typeToMappingInfo); } } private void setTypeMappingInfoToJavaTypeAdapaters(Map typeMappingInfoToAdapters) { this.typeMappingInfoToJavaTypeAdapters = typeMappingInfoToAdapters; } private void setQNamesToDeclaredClasses(HashMap> nameToDeclaredClasses) { qNamesToDeclaredClasses = nameToDeclaredClasses; } private void setQNameToGeneratedClasses(Map> qNameToClass) { this.qNameToGeneratedClasses = qNameToClass; } public void setXMLContext(XMLContext xmlContext) { this.xmlContext = xmlContext; } public JAXBMarshaller createMarshaller(JAXBContext jaxbContext) throws jakarta.xml.bind.JAXBException { // create a JAXBIntrospector and set it on the marshaller JAXBMarshaller marshaller = new JAXBMarshaller(xmlContext.createMarshaller(), jaxbContext); if (generator != null && generator.hasMarshalCallbacks()) { // initialize each callback in the map ClassLoader classLoader = getXMLContext().getSession(0).getDatasourcePlatform().getConversionManager().getLoader(); for (Object o : generator.getMarshalCallbacks().keySet()) { MarshalCallback cb = generator.getMarshalCallbacks().get(o); cb.initialize(classLoader); } marshaller.setMarshalCallbacks(generator.getMarshalCallbacks()); } if (properties != null) { setPropertyOnMarshaller(JAXBContextProperties.MEDIA_TYPE, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, marshaller); setPropertyOnMarshaller(JAXBContextProperties.NAMESPACE_PREFIX_MAPPER, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_INCLUDE_ROOT, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_VALUE_WRAPPER, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_NAMESPACE_SEPARATOR, marshaller); setPropertyOnMarshaller(JAXBContextProperties.OBJECT_GRAPH, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, marshaller); setPropertyOnMarshaller(JAXBContextProperties.BEAN_VALIDATION_MODE, marshaller); setPropertyOnMarshaller(JAXBContextProperties.BEAN_VALIDATION_FACTORY, marshaller); setPropertyOnMarshaller(JAXBContextProperties.BEAN_VALIDATION_GROUPS, marshaller); setPropertyOnMarshaller(JAXBContextProperties.BEAN_VALIDATION_NO_OPTIMISATION, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_TYPE_COMPATIBILITY, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_USE_XSD_TYPES_WITH_PREFIX, marshaller); setPropertyOnMarshaller(JAXBContextProperties.JSON_TYPE_ATTRIBUTE_NAME, marshaller); setPropertyOnMarshaller(JAXBContextProperties.MOXY_LOGGING_LEVEL, marshaller); setPropertyOnMarshaller(JAXBContextProperties.MOXY_LOG_PAYLOAD, marshaller); } return marshaller; } public JAXBUnmarshaller createUnmarshaller(JAXBContext jaxbContext) throws jakarta.xml.bind.JAXBException { JAXBUnmarshaller unmarshaller = new JAXBUnmarshaller(xmlContext.createUnmarshaller(PARSER_FEATURES), jaxbContext); if (generator != null && generator.hasUnmarshalCallbacks()) { // initialize each callback in the map ClassLoader classLoader = getXMLContext().getSession(0).getDatasourcePlatform().getConversionManager().getLoader(); for (Object o : generator.getUnmarshalCallbacks().keySet()) { UnmarshalCallback cb = generator.getUnmarshalCallbacks().get(o); cb.initialize(classLoader); } unmarshaller.setUnmarshalCallbacks(generator.getUnmarshalCallbacks()); } if (properties != null) { setPropertyOnUnmarshaller(JAXBContextProperties.MEDIA_TYPE, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.NAMESPACE_PREFIX_MAPPER, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_INCLUDE_ROOT, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_VALUE_WRAPPER, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_NAMESPACE_SEPARATOR, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.OBJECT_GRAPH, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.UNMARSHALLING_CASE_INSENSITIVE, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.BEAN_VALIDATION_MODE, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.BEAN_VALIDATION_FACTORY, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.BEAN_VALIDATION_GROUPS, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.BEAN_VALIDATION_NO_OPTIMISATION, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_TYPE_COMPATIBILITY, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_USE_XSD_TYPES_WITH_PREFIX, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.JSON_TYPE_ATTRIBUTE_NAME, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.MOXY_LOGGING_LEVEL, unmarshaller); setPropertyOnUnmarshaller(JAXBContextProperties.MOXY_LOG_PAYLOAD, unmarshaller); } return unmarshaller; } public JAXBBinder createBinder(JAXBContext context) { XMLMarshaller marshaller = null; XMLUnmarshaller unmarshaller = null; try { marshaller = createMarshaller(context).getXMLMarshaller(); unmarshaller = createUnmarshaller(context).getXMLUnmarshaller(); } catch (jakarta.xml.bind.JAXBException e) { // log something marshaller = context.getXMLContext().createMarshaller(); unmarshaller = context.getXMLContext().createUnmarshaller(); } return new JAXBBinder(context, marshaller, unmarshaller); } private void setPropertyOnMarshaller(String propertyName, JAXBMarshaller marshaller) throws PropertyException { Object propertyValue = properties.get(propertyName); if (propertyValue != null) { marshaller.setProperty(propertyName, propertyValue); } } private void setPropertyOnUnmarshaller(String propertyName, JAXBUnmarshaller unmarshaller) throws PropertyException { Object propertyValue = properties.get(propertyName); if (propertyValue != null) { unmarshaller.setProperty(propertyName, propertyValue); } } } private static void enableFacetsIfPropertySetTrue(JavaModelInputImpl inputImpl, Map properties) { Object propertyValue = properties.get(JAXBContextProperties.BEAN_VALIDATION_FACETS); if (propertyValue != null) inputImpl.setFacets((Boolean) propertyValue); } private static void openToCore(JavaModelInputImpl input) { JavaClass[] javaClasses = input.getJavaClasses(); Set> classes = new HashSet<>(); for (JavaClass jc: javaClasses) { classes.add(((JavaClassImpl) jc).getJavaClass()); } openToCore(classes); } //we need to open what has been opened to us either directly //or by jakarta.xml.bind API also to core on which we depend //to allow reflection access to code we use from that module private static void openToCore(Collection> classes) { // need to open to core IF we're not eclipselink.jar if (NEEDS_OPEN) { final Module moxyModule = JAXBContext.class.getModule(); final Module coreModule = Version.class.getModule(); for (Class cls : classes) { Class jaxbClass = cls.isArray() ? cls.getComponentType() : cls; final Module classModule = jaxbClass.getModule(); final String packageName = jaxbClass.getPackageName(); //no need for unnamed and java.base types if (!classModule.isNamed() || classModule.getName().equals("java.base")) { continue; } //propagate openness to o.e.p.core module if (classModule.isOpen(packageName, moxyModule) && !classModule.isOpen(packageName, coreModule)) { classModule.addOpens(packageName, coreModule); AbstractSessionLog.getLog().log(SessionLog.FINE, SessionLog.MOXY, "open_pkg", packageName, classModule.getName(), coreModule.getName()); } } } } private static final boolean NEEDS_OPEN; static { boolean b = false; try { b = JAXBContext.class.getModule() != Version.class.getModule(); } catch (NoSuchMethodError nsme) { //android b = false; } NEEDS_OPEN = b; } }