| /* |
| * Copyright (c) 1998, 2021 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 |
| package org.eclipse.persistence.oxm; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.core.mappings.CoreAttributeAccessor; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.exceptions.XMLMarshalException; |
| import org.eclipse.persistence.internal.oxm.Context; |
| import org.eclipse.persistence.internal.oxm.ConversionManager; |
| import org.eclipse.persistence.internal.oxm.XPathFragment; |
| import org.eclipse.persistence.internal.oxm.XPathQName; |
| import org.eclipse.persistence.internal.oxm.accessor.OrmAttributeAccessor; |
| import org.eclipse.persistence.internal.oxm.documentpreservation.DescriptorLevelDocumentPreservationPolicy; |
| import org.eclipse.persistence.internal.oxm.documentpreservation.NoDocumentPreservationPolicy; |
| import org.eclipse.persistence.internal.oxm.mappings.CompositeCollectionMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.CompositeObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Descriptor; |
| import org.eclipse.persistence.internal.oxm.mappings.Mapping; |
| import org.eclipse.persistence.internal.oxm.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedGetClassLoaderForClass; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.OneToOneMapping; |
| import org.eclipse.persistence.oxm.documentpreservation.DocumentPreservationPolicy; |
| import org.eclipse.persistence.oxm.platform.SAXPlatform; |
| import org.eclipse.persistence.oxm.platform.XMLPlatform; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.sessions.Project; |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.sessions.SessionEventListener; |
| import org.eclipse.persistence.sessions.SessionEventManager; |
| import org.eclipse.persistence.sessions.factories.SessionManager; |
| import org.eclipse.persistence.sessions.factories.XMLSessionConfigLoader; |
| |
| /** |
| * <p> |
| * An XMLContext is created based on EclipseLink sessions or projects and can then |
| * used to create instances of XMLMarshaller, XMLUnmarshaller and XMLValidator. |
| * |
| * <p> |
| * There are constructors to create an XMLContext with a single EclipseLink project |
| * or with a String which is a single EclipseLink session name or a ':' separated |
| * list of EclipseLink session names. |
| * |
| * <p> |
| * <em>Code Sample</em><br> |
| * <code> |
| * XMLContext context = new XMLContext("mySessionName");<br> |
| * XMLMarshaller marshaller = context.createMarshaller();<br> |
| * XMLUnmarshaller unmarshaller = context.createUnmarshaller();<br> |
| * XMLValidator validator = context.createValidator();<br> |
| * </code> |
| * |
| * <p>The XMLContext is thread-safe. If multiple threads accessing the same XMLContext object |
| * request an XMLMarshaller, each will receive their own instance of XMLMarshaller, so any |
| * state that the XMLMarshaller maintains will be unique to that process. The same is true |
| * of instances of XMLUnmarshaller and XMLValidator. |
| * |
| * @see org.eclipse.persistence.oxm.XMLMarshaller |
| * @see org.eclipse.persistence.oxm.XMLUnmarshaller |
| * @see org.eclipse.persistence.oxm.XMLValidator |
| * |
| */ |
| public class XMLContext extends Context<AbstractSession, XMLDescriptor, XMLField, NamespaceResolver, Project, DatabaseSession, SessionEventListener> { |
| |
| |
| XMLContext(XMLContextState xmlContextState) { |
| this.contextState = xmlContextState; |
| } |
| |
| /** |
| * Create a new XMLContext based on the specified session name or list of |
| * session names |
| * |
| * @param sessionNames |
| * A single session name or multiple session names separated by a : |
| */ |
| public XMLContext(String sessionNames) { |
| this(sessionNames, privilegedGetClassLoaderForClass(XMLContext.class)); |
| } |
| |
| /** |
| * Create a new XMLContext based on the specified session name or list of |
| * session names |
| * |
| * @param sessionNames |
| * A single session name or multiple session names separated by a : |
| * @param classLoader |
| * classloader for loading sessions.xml |
| */ |
| public XMLContext(String sessionNames, ClassLoader classLoader) { |
| this(sessionNames, classLoader, null); |
| } |
| |
| /** |
| * Create a new XMLContext based on passed in session names and session meta |
| * XML. |
| * |
| * @param sessionNames |
| * A single session name or multiple session names separated by |
| * a: |
| * @param xmlResource |
| * path to XML file containing session meta data to initialize |
| * and load sessions. |
| */ |
| public XMLContext(String sessionNames, String xmlResource) { |
| this(sessionNames, privilegedGetClassLoaderForClass(XMLContext.class), xmlResource); |
| } |
| |
| /** |
| * Create a new XMLContext based on passed in session names, classloader and |
| * session meta XML. |
| * |
| * @param sessionNames |
| * A single session name or multiple session names separated by a : |
| * @param classLoader |
| * classloader for loading sessions.xml |
| * @param xmlResource |
| * path to XML file containing session meta data to initialize |
| * and load sessions. |
| */ |
| public XMLContext(String sessionNames, ClassLoader classLoader, String xmlResource) { |
| contextState = new XMLContextState(this, sessionNames, classLoader, xmlResource); |
| } |
| |
| /** |
| * Create a new XMLContext based on the specified project |
| * |
| * @param project |
| * An EclipseLink project |
| */ |
| public XMLContext(Project project) { |
| this(project, Thread.currentThread().getContextClassLoader()); |
| } |
| |
| /** |
| * Create a new XMLContext based on the specified Project and ClassLoader. |
| * |
| * @param project An EclipseLink project |
| * @param classLoader The ClassLoader to be used |
| */ |
| public XMLContext(Project project, ClassLoader classLoader) { |
| this (project, classLoader, (Collection<SessionEventListener>) null); |
| } |
| |
| /** |
| * Create a new XMLContext based on the specified Project and ClassLoader. |
| * |
| * @param project An EclipseLink project |
| * @param classLoader The ClassLoader to be used |
| * @param sessionEventListener If non-null, this listener will be registered with the SessionEventManager |
| * @see SessionEventListener |
| * @see SessionEventManager |
| */ |
| public XMLContext(Project project, ClassLoader classLoader, SessionEventListener sessionEventListener) { |
| Collection<SessionEventListener> sessionEventListeners = new ArrayList<>(1); |
| sessionEventListeners.add(sessionEventListener); |
| contextState = new XMLContextState(this, project, classLoader, sessionEventListeners); |
| } |
| |
| /** |
| * Create a new XMLContext based on the specified Project and ClassLoader. |
| * |
| * @param project An EclipseLink project |
| * @param classLoader The ClassLoader to be used |
| * @param sessionEventListeners If non-null, these listeners will be registered with the SessionEventManager |
| * @see SessionEventListener |
| * @see SessionEventManager |
| */ |
| public XMLContext(Project project, ClassLoader classLoader, Collection<SessionEventListener> sessionEventListeners) { |
| contextState = new XMLContextState(this, project, classLoader, sessionEventListeners); |
| } |
| |
| public XMLContext(Collection projects) { |
| this(projects, Thread.currentThread().getContextClassLoader()); |
| } |
| |
| public XMLContext(Collection projects, ClassLoader classLoader) { |
| contextState = new XMLContextState(this, projects, classLoader); |
| } |
| |
| /** |
| * INTERNAL: Return the XMLContextState that represents the XMLContexts |
| * stateful information. This method is provided for the benefits of layers |
| * that build on top of the core OXM layer such as MOXy's JAXB |
| * implementation. |
| */ |
| public XMLContextState getXMLContextState() { |
| return (XMLContextState) contextState; |
| } |
| |
| /** |
| * INTERNAL: Set the stateful information for this XMLContext. Once the new |
| * state has been set, OXM operations (marshal, unmarshal, etc) will be |
| * based on this new state. This method is provided for the benefit of |
| * layers that build on top of the core OXM layer such as MOXy's JAXB |
| * |
| */ |
| public void setXMLContextState(XMLContextState xcs) { |
| synchronized(this) { |
| this.contextState = xcs; |
| } |
| } |
| |
| /** |
| * INTERNAL: Add and initialize a new session to the list of sessions |
| * associated with this XMLContext. |
| */ |
| public void addSession(DatabaseSession sessionToAdd) { |
| getXMLContextState().addSession(sessionToAdd); |
| } |
| |
| /** |
| * Create a new XMLUnmarshaller |
| * |
| * @return An XMLUnmarshaller based on this XMLContext |
| */ |
| @Override |
| public XMLUnmarshaller createUnmarshaller() { |
| return new XMLUnmarshaller(this); |
| } |
| |
| /** |
| * Create a new XMLUnmarshaller |
| * <pre> |
| * {@literal Map<String, Boolean> parserFeatures = new HashMap<String, Boolean>(1);} |
| * parserFeatures.put("http://apache.org/xml/features/validation/schema/normalized-value", false); |
| * XMLUnmarshaller unmarshaller = xmlContext.createUnmarshaller(parserFeatures); |
| * </pre> |
| * @return An XMLUnmarshaller based on this XMLContext, the underlying |
| * parser will use the passed in parser features. |
| */ |
| public XMLUnmarshaller createUnmarshaller(Map<String, Boolean> parserFeatures) { |
| return new XMLUnmarshaller(this, parserFeatures); |
| } |
| |
| /** |
| * Create a new XMLBinder |
| * @return an XMLBinder based on this XMLContext |
| */ |
| public XMLBinder createBinder() { |
| return new XMLBinder(this); |
| } |
| |
| /** |
| * Create a new XMLBinder with supplied marshaller and unmarshaller instances. |
| * @return an XMLBinder based on this XMLContext, |
| */ |
| public XMLBinder createBinder(XMLMarshaller marshaller, XMLUnmarshaller unmarshaller) { |
| return new XMLBinder(this, marshaller, unmarshaller); |
| } |
| |
| /** |
| * Create a new XMLMarshaller |
| * |
| * @return An XMLMarshaller based on this XMLContext |
| */ |
| @Override |
| public XMLMarshaller createMarshaller() { |
| return new XMLMarshaller(this); |
| } |
| |
| /** |
| * Create a new XMLValidator |
| * |
| * @return An XMLValidator based on this XMLContext |
| */ |
| public XMLValidator createValidator() { |
| return new XMLValidator(this); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this object. Since the |
| * object may be mapped by more that one of the projects used to create the |
| * XML Context, this method will return the first match. |
| * |
| * The session will be a unit of work if document preservation is not |
| * enabled. This method will typically be used for unmarshalling |
| * when a non-shared cache is desired. |
| */ |
| public AbstractSession getReadSession(Object object) { |
| return getSession(object); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the session corresponding to this class. Since the class |
| * may be mapped by more that one of the projects used to create the XML |
| * Context, this method will return the first match. |
| * |
| * The session will be a unit of work if document preservation is not |
| * enabled. This method will typically be used for unmarshalling |
| * when a non-shared cache is desired. |
| */ |
| public AbstractSession getReadSession(Class clazz) { |
| return super.getSession(clazz); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the session corresponding to this XMLDescriptor. Since |
| * the class may be mapped by more that one of the projects used to create |
| * the XML Context, this method will return the first match. |
| * |
| * The session will be a unit of work if document preservation is not |
| * enabled. This method will typically be used for unmarshalling |
| * when a non-shared cache is desired. |
| */ |
| public AbstractSession getReadSession(XMLDescriptor xmlDescriptor) { |
| return super.getSession(xmlDescriptor); |
| } |
| |
| /** |
| * INTERNAL: Return the EclipseLink session used to marshal. |
| */ |
| public List getSessions() { |
| return getXMLContextState().getSessions(); |
| } |
| |
| /** |
| * INTERNAL: <code> |
| * XMLContext xmlContext = new XMLContext("path0:path1");<br> |
| * DatabaseSession session = xmlContext.getSession(0); // returns session for path0<br> |
| * </code> |
| */ |
| public DatabaseSession getSession(int index) { |
| return getXMLContextState().getSession(index); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this object. Since the |
| * object may be mapped by more that one of the projects used to create the |
| * XML Context, this method will return the first match. |
| */ |
| @Override |
| public AbstractSession getSession(Object object) { |
| return super.getSession(object); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this class. Since the class |
| * may be mapped by more that one of the projects used to create the XML |
| * Context, this method will return the first match. |
| */ |
| @Override |
| public AbstractSession getSession(Class clazz) { |
| return super.getSession(clazz); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this XMLDescriptor. Since |
| * the class may be mapped by more that one of the projects used to create |
| * the XML Context, this method will return the first match. |
| */ |
| @Override |
| public AbstractSession getSession(XMLDescriptor xmlDescriptor) { |
| return super.getSession(xmlDescriptor); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void storeXMLDescriptorByQName(XMLDescriptor xmlDescriptor) { |
| contextState.storeDescriptorByQName(xmlDescriptor, null, null); |
| } |
| |
| /** |
| * INTERNAL: Return the XMLDescriptor with the default root mapping matching |
| * the QName parameter. |
| */ |
| @Override |
| public XMLDescriptor getDescriptor(QName qName) { |
| return super.getDescriptor(qName); |
| } |
| |
| /** |
| * INTERNAL: Return the XMLDescriptor with the default root mapping matching |
| * the QName parameter. |
| */ |
| @Override |
| public XMLDescriptor getDescriptor(XPathQName xpathQName) { |
| return super.getDescriptor(xpathQName); |
| } |
| |
| public void addDescriptorByQName(QName qName, XMLDescriptor descriptor) { |
| contextState.addDescriptorByQName(qName, descriptor); |
| } |
| |
| /** |
| * INTERNAL: Return the XMLDescriptor mapped to the global type matching the |
| * XPathFragment parameter. |
| */ |
| @Override |
| public XMLDescriptor getDescriptorByGlobalType(XPathFragment xPathFragment) { |
| return super.getDescriptorByGlobalType(xPathFragment); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the DocumentPreservationPolicy associated with this session |
| */ |
| public DocumentPreservationPolicy getDocumentPreservationPolicy(AbstractSession session) { |
| if (session == null) { |
| return null; |
| } |
| XMLLogin login = (XMLLogin) session.getDatasourceLogin(); |
| return login.getDocumentPreservationPolicy(); |
| } |
| |
| public void setupDocumentPreservationPolicy(DatabaseSession session) { |
| getXMLContextState().setupDocumentPreservationPolicy(session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if any session held onto by this context has a document preservation |
| * policy that requires unmarshalling from a Node. |
| */ |
| @Override |
| public boolean hasDocumentPreservation() { |
| return getXMLContextState().hasDocumentPreservation(); |
| } |
| |
| /** |
| * ADVANCED: |
| * Adjust the OXM metadata to take into account ORM mapping metadata, |
| */ |
| public void applyORMMetadata(AbstractSession ormSession) { |
| //Iterate over the ORM descriptors and check for matching OXM descriptors |
| Iterator<ClassDescriptor> ormDescriptors = ormSession.getDescriptors().values().iterator(); |
| while(ormDescriptors.hasNext()) { |
| ClassDescriptor ormDescriptor = ormDescriptors.next(); |
| Class javaClass = ormDescriptor.getJavaClass(); |
| AbstractSession oxmSession = null; |
| try { |
| oxmSession = this.getSession(javaClass); |
| } catch(XMLMarshalException ex) { |
| //if we couldn't find a session for this class, we |
| //don't have an OX descriptor for it. |
| } |
| if(oxmSession != null) { |
| ClassDescriptor oxmDescriptor = oxmSession.getDescriptor(javaClass); |
| //If we have an oxmDescriptor for this ORM descriptor, iterate over |
| //mappings, and update the required OXM mappings attribute accessors |
| Iterator<DatabaseMapping> ormMappings = ormDescriptor.getMappings().iterator(); |
| while(ormMappings.hasNext()) { |
| DatabaseMapping ormMapping = ormMappings.next(); |
| Mapping oxmMapping = (Mapping) oxmDescriptor.getMappingForAttributeName(ormMapping.getAttributeName()); |
| if(oxmMapping != null) { |
| CoreAttributeAccessor oxmAccessor = oxmMapping.getAttributeAccessor(); |
| OrmAttributeAccessor newAccessor = new OrmAttributeAccessor(ormMapping.getAttributeAccessor(), oxmAccessor); |
| if(ormMapping.isOneToOneMapping() && ((OneToOneMapping)ormMapping).usesIndirection()) { |
| newAccessor.setValueHolderProperty(true); |
| } |
| newAccessor.setChangeTracking(ormDescriptor.getObjectChangePolicy().isAttributeChangeTrackingPolicy()); |
| oxmMapping.setAttributeAccessor(newAccessor); |
| |
| //check to see if we need to deal with containerAccessor |
| CoreAttributeAccessor containerAccessor = null; |
| Class containerClass = null; |
| if(oxmMapping.isAbstractCompositeObjectMapping()) { |
| containerAccessor = ((CompositeObjectMapping)oxmMapping).getInverseReferenceMapping().getAttributeAccessor(); |
| containerClass = ((CompositeObjectMapping)oxmMapping).getReferenceClass(); |
| } else if(oxmMapping.isAbstractCompositeCollectionMapping()) { |
| containerAccessor = ((CompositeCollectionMapping)oxmMapping).getInverseReferenceMapping().getAttributeAccessor(); |
| containerClass = ((CompositeCollectionMapping)oxmMapping).getReferenceClass(); |
| } |
| if(containerAccessor != null) { |
| ClassDescriptor containerDescriptor = ormSession.getDescriptor(containerClass); |
| if(containerDescriptor != null) { |
| DatabaseMapping ormContainerMapping = containerDescriptor.getMappingForAttributeName(containerAccessor.getAttributeName()); |
| if(ormContainerMapping != null) { |
| //Check for indirection on the container mapping |
| OrmAttributeAccessor ormAccessor = new OrmAttributeAccessor(ormContainerMapping.getAttributeAccessor(), containerAccessor); |
| ormAccessor.setChangeTracking(containerDescriptor.getObjectChangePolicy().isAttributeChangeTrackingPolicy()); |
| ormAccessor.setValueHolderProperty(ormContainerMapping instanceof OneToOneMapping && ((OneToOneMapping)ormContainerMapping).usesIndirection()); |
| if(oxmMapping.isAbstractCompositeObjectMapping()) { |
| ((CompositeObjectMapping)oxmMapping).getInverseReferenceMapping().setAttributeAccessor(ormAccessor); |
| } else if(oxmMapping.isAbstractCompositeCollectionMapping()) { |
| ((CompositeCollectionMapping)oxmMapping).getInverseReferenceMapping().setAttributeAccessor(ormAccessor); |
| } |
| |
| } |
| } |
| } |
| } |
| } |
| Iterator<DatabaseMapping> oxmMappingsIterator = oxmDescriptor.getMappings().iterator(); |
| while(oxmMappingsIterator.hasNext()) { |
| //iterate over the oxm mappings. Any ReferenceMappings that have a |
| //collection as a backpointer, check to see if the container policy |
| //needs to be matched with the ORM project |
| DatabaseMapping nextMapping = oxmMappingsIterator.next(); |
| if(nextMapping instanceof ObjectReferenceMapping) { |
| ObjectReferenceMapping refMapping = (ObjectReferenceMapping)nextMapping; |
| if(refMapping.getInverseReferenceMapping().getAttributeAccessor() != null && refMapping.getInverseReferenceMapping().getContainerPolicy() != null) { |
| ClassDescriptor refDescriptor = ormSession.getClassDescriptor(refMapping.getReferenceClass()); |
| if(refDescriptor != null) { |
| DatabaseMapping backpointerMapping =refDescriptor.getMappingForAttributeName(refMapping.getInverseReferenceMapping().getAttributeName()); |
| if(backpointerMapping != null && backpointerMapping.isCollectionMapping()) { |
| refMapping.getInverseReferenceMapping().getContainerPolicy().setContainerClass(backpointerMapping.getContainerPolicy().getContainerClass()); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * <p>Query the object model based on the corresponding XML document. The following pairings are equivalent:</p> |
| * |
| * <i>Return the Customer's ID</i> |
| * <pre> Integer id = xmlContext.getValueByXPath(customer, "@id", null, Integer.class); |
| * Integer id = customer.getId();</pre> |
| * |
| * <i>Return the Customer's Name</i> |
| * <pre> String name = xmlContext.getValueByXPath(customer, "ns:personal-info/ns:name/text()", null, String.class); |
| * String name = customer.getName();</pre> |
| * |
| * <i>Return the Customer's Address</i> |
| * <pre> Address address = xmlContext.getValueByXPath(customer, "ns:contact-info/ns:address", aNamespaceResolver, Address.class); |
| * Address address = customer.getAddress();</pre> |
| * |
| * <i>Return all the Customer's PhoneNumbers</i> |
| * <pre> List phoneNumbers = xmlContext.getValueByXPath(customer, "ns:contact-info/ns:phone-number", aNamespaceResolver, List.class); |
| * List phoneNumbers = customer.getPhoneNumbers();</pre> |
| * |
| * <i>Return the Customer's second PhoneNumber</i> |
| * <pre> PhoneNumber phoneNumber = xmlContext.getValueByXPath(customer, "ns:contact-info/ns:phone-number[2]", aNamespaceResolver, PhoneNumber.class); |
| * PhoneNumber phoneNumber = customer.getPhoneNumbers().get(1);</pre> |
| * |
| * <i>Return the base object</i> |
| * <pre> Customer customer = xmlContext.getValueByXPath(customer, ".", aNamespaceResolver, Customer.class); |
| * Customer customer = customer; |
| * </pre> |
| * |
| * @param <T> 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. |
| */ |
| @Override |
| public <T> T getValueByXPath(Object object, String xPath, NamespaceResolver namespaceResolver, Class<T> returnType) { |
| return super.getValueByXPath(object, xPath, namespaceResolver, returnType); |
| } |
| |
| /** |
| * <p>Set values in the object model based on the corresponding XML document. The following pairings are equivalent:</p> |
| * |
| * <i>Set the Customer's ID</i> |
| * <pre> xmlContext.setValueByXPath(customer, "@id", null, Integer.valueOf(123)); |
| * customer.setId(Integer.valueOf(123));</pre> |
| * |
| * <i>Set the Customer's Name</i> |
| * <pre> xmlContext.setValueByXPath(customer, "ns:personal-info/ns:name/text()", aNamespaceResolver, "Jane Doe"); |
| * customer.setName("Jane Doe");</pre> |
| * |
| * <i>Set the Customer's Address</i> |
| * <pre> xmlContext.setValueByXPath(customer, "ns:contact-info/ns:address", aNamespaceResolver, anAddress); |
| * customer.setAddress(anAddress);</pre> |
| * |
| * <i>Set the Customer's PhoneNumbers</i> |
| * <pre> xmlContext.setValueByXPath(customer, "ns:contact-info/ns:phone-number", aNamespaceResolver, phoneNumbers); |
| * customer.setPhoneNumbers(phoneNumbers);</pre> |
| * |
| * <i>Set the Customer's second PhoneNumber</i> |
| * <pre> xmlContext.setValueByXPath(customer, "ns:contact-info/ns:phone-number[2]", aNamespaceResolver, aPhoneNumber); |
| * customer.getPhoneNumbers().get(1);</pre> |
| * |
| * @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. |
| */ |
| @Override |
| public void setValueByXPath(Object object, String xPath, NamespaceResolver namespaceResolver, Object value) { |
| super.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) throws IllegalArgumentException { |
| QName qName = new QName(namespace, typeName); |
| XMLDescriptor d = null; |
| if (!isGlobalType) { |
| d = getDescriptor(qName); |
| } else { |
| XPathFragment frag = new XPathFragment(); |
| frag.setNamespaceURI(namespace); |
| frag.setLocalName(typeName); |
| |
| d = getDescriptorByGlobalType(frag); |
| } |
| |
| if (d == null) { |
| return null; |
| } |
| |
| return d.getInstantiationPolicy().buildNewInstance(); |
| } |
| |
| /** |
| * Create a new object instance for a given XPath, relative to the parentObject. |
| * |
| * @param <T> |
| * 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. |
| */ |
| @Override |
| public <T> T createByXPath(Object parentObject, String xPath, NamespaceResolver namespaceResolver, Class<T> returnType) { |
| return super.createByXPath(parentObject, xPath, namespaceResolver, returnType); |
| } |
| |
| |
| private static class XMLContextState extends ContextState<AbstractSession, XMLDescriptor, Project, DatabaseSession, SessionEventListener> { |
| |
| private List<DatabaseSession> sessions; |
| private boolean hasDocumentPreservation = false; |
| |
| protected XMLContextState(XMLContext xmlContext, Collection<Project> projects, ClassLoader classLoader) { |
| this.context = xmlContext; |
| sessions = new ArrayList(projects.size()); |
| Iterator<Project> iterator = projects.iterator(); |
| while (iterator.hasNext()) { |
| Project project = iterator.next(); |
| preLogin(project, classLoader); |
| DatabaseSession session = project.createDatabaseSession(); |
| |
| // turn logging for this session off and leave the global |
| // session up |
| // Note: setting level to SEVERE or WARNING will printout |
| // stacktraces for expected exceptions |
| session.setLogLevel(SessionLog.OFF); |
| // don't turn off global static logging |
| // AbstractSessionLog.getLog().log(AbstractSessionLog.INFO, |
| // "ox_turn_global_logging_off", getClass()); |
| // AbstractSessionLog.getLog().setLevel(AbstractSessionLog.OFF); |
| setupSession(session); |
| sessions.add(session); |
| storeDescriptorsByQName(session); |
| } |
| } |
| |
| private XMLContextState(XMLContext xmlContext, String sessionNames, ClassLoader classLoader, String xmlResource) { |
| super(); |
| this.context = xmlContext; |
| XMLSessionConfigLoader loader = null; |
| if (xmlResource != null) { |
| loader = new XMLSessionConfigLoader(xmlResource); |
| } else { |
| loader = new XMLSessionConfigLoader(); |
| } |
| descriptorsByQName = new HashMap(); |
| descriptorsByGlobalType = new HashMap(); |
| StringTokenizer st = new StringTokenizer(sessionNames, ":"); |
| sessions = new ArrayList(st.countTokens()); |
| int index = 0; |
| while (st.hasMoreTokens()) { |
| sessions.add(buildSession(st.nextToken(), classLoader, loader)); |
| index++; |
| } |
| for (int x = index - 1; x >= 0; x--) { |
| storeDescriptorsByQName(sessions.get(x)); |
| } |
| } |
| |
| protected XMLContextState(XMLContext xmlContext, Project project, |
| ClassLoader classLoader, |
| Collection<SessionEventListener> sessionEventListeners) { |
| super(xmlContext, project, classLoader, sessionEventListeners); |
| } |
| |
| /** |
| * INTERNAL: Add and initialize a new session to the list of sessions |
| * associated with this XMLContext. |
| */ |
| private void addSession(DatabaseSession sessionToAdd) { |
| if ((sessionToAdd.getDatasourceLogin() == null) || !(sessionToAdd.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) { |
| XMLPlatform<org.eclipse.persistence.internal.oxm.XMLUnmarshaller> platform = new SAXPlatform(); |
| sessionToAdd.setLogin(new XMLLogin(platform)); |
| } |
| DatabaseSession session = sessionToAdd.getProject().createDatabaseSession(); |
| if (sessionToAdd.getEventManager().hasListeners()) { |
| List<SessionEventListener> listeners = sessionToAdd.getEventManager().getListeners(); |
| int listenersSize = listeners.size(); |
| for (int x = 0; x < listenersSize; x++) { |
| session.getEventManager().addListener(listeners.get(x)); |
| } |
| } |
| session.setExceptionHandler(sessionToAdd.getExceptionHandler()); |
| session.setLogLevel(SessionLog.OFF); |
| setupSession(session); |
| if(null == sessions) { |
| sessions = new ArrayList<>(); |
| } |
| if(null != this.session) { |
| sessions.add(this.session); |
| } |
| sessions.add(session); |
| |
| storeDescriptorsByQName(session); |
| } |
| |
| @Override |
| protected void setupSession(DatabaseSession session) { |
| session.setLogLevel(SessionLog.OFF); |
| session.login(); |
| setupDocumentPreservationPolicy(session); |
| } |
| |
| private DatabaseSession buildSession(String sessionName, ClassLoader classLoader, XMLSessionConfigLoader sessionLoader) throws XMLMarshalException { |
| DatabaseSession dbSession; |
| if (classLoader != null) { |
| dbSession = (DatabaseSession) SessionManager.getManager().getSession(sessionLoader, sessionName, classLoader, false, true); |
| } else { |
| dbSession = (DatabaseSession) SessionManager.getManager().getSession(sessionLoader, sessionName, privilegedGetClassLoaderForClass(this.getClass()), false, false, false); |
| } |
| if ((dbSession.getDatasourceLogin() == null) || !(dbSession.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) { |
| XMLPlatform<org.eclipse.persistence.internal.oxm.XMLUnmarshaller> platform = new SAXPlatform(); |
| dbSession.setLogin(new XMLLogin(platform)); |
| } |
| DatabaseSession session = dbSession.getProject().createDatabaseSession(); |
| if (dbSession.getEventManager().hasListeners()) { |
| List<SessionEventListener> listeners = dbSession.getEventManager().getListeners(); |
| int listenersSize = listeners.size(); |
| for (int x = 0; x < listenersSize; x++) { |
| session.getEventManager().addListener(listeners.get(x)); |
| } |
| } |
| session.setExceptionHandler(dbSession.getExceptionHandler()); |
| session.setLogLevel(SessionLog.OFF); |
| setupDocumentPreservationPolicy(session); |
| session.login(); |
| return session; |
| } |
| |
| @Override |
| protected DatabaseSession getSession() { |
| if(null == sessions) { |
| return super.getSession(); |
| } |
| return sessions.get(0); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this class. Since the class |
| * may be mapped by more that one of the projects used to create the XML |
| * Context, this method will return the first match. |
| */ |
| @Override |
| protected AbstractSession getSession(Class clazz) { |
| if (null == clazz) { |
| return null; |
| } |
| if (null == sessions) { |
| return super.getSession(clazz); |
| } |
| for (int x = 0, numberOfSessions = sessions.size(); x < numberOfSessions; x++) { |
| AbstractSession next = ((AbstractSession) sessions.get(x)); |
| if (next.getDescriptor(clazz) != null) { |
| return next; |
| } |
| } |
| throw XMLMarshalException.descriptorNotFoundInProject(clazz.getName()); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this XMLDescriptor. Since |
| * the class may be mapped by more that one of the projects used to create |
| * the XML Context, this method will return the first match. |
| */ |
| @Override |
| protected AbstractSession getSession(XMLDescriptor xmlDescriptor) { |
| if (null == xmlDescriptor) { |
| return null; |
| } |
| if(null == sessions) { |
| return super.getSession(xmlDescriptor); |
| } |
| for (int x = 0, numberOfSessions = sessions.size(); x < numberOfSessions; x++) { |
| AbstractSession next = ((AbstractSession) sessions.get(x)); |
| if (next.getProject().getOrderedDescriptors().contains(xmlDescriptor)) { |
| return next; |
| } |
| } |
| throw XMLMarshalException.descriptorNotFoundInProject(xmlDescriptor.getJavaClass().getName()); |
| } |
| |
| private List<DatabaseSession> getSessions() { |
| if(null == sessions) { |
| return Collections.singletonList(session); |
| } |
| return sessions; |
| } |
| |
| /** |
| * INTERNAL: <code> |
| * XMLContext xmlContext = new XMLContext("path0:path1");<br> |
| * DatabaseSession session = xmlContext.getSession(0); // returns session for path0<br> |
| * </code> |
| */ |
| private DatabaseSession getSession(int index) { |
| if (null == sessions) { |
| return session; |
| } |
| return sessions.get(index); |
| } |
| |
| /** |
| * INTERNAL: Return the session corresponding to this object. Since the |
| * object may be mapped by more that one of the projects used to create the |
| * XML Context, this method will return the first match. |
| */ |
| @Override |
| protected AbstractSession getSession(Object object) { |
| if (null == object) { |
| return null; |
| } |
| if (null == sessions) { |
| return super.getSession(object); |
| } |
| for (int x = 0, numberOfSessions = sessions.size(); x < numberOfSessions; x++) { |
| AbstractSession next = ((AbstractSession) sessions.get(x)); |
| if (next.getDescriptor(object) != null) { |
| return next; |
| } |
| } |
| throw XMLMarshalException.descriptorNotFoundInProject(object.getClass().getName()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if any session held onto by this context has a document preservation |
| * policy that requires unmarshalling from a Node. |
| */ |
| private boolean hasDocumentPreservation() { |
| return this.hasDocumentPreservation; |
| } |
| |
| @Override |
| protected void preLogin(Project project, ClassLoader classLoader) { |
| if ((project.getDatasourceLogin() == null) || !(project.getDatasourceLogin().getDatasourcePlatform() instanceof XMLPlatform)) { |
| XMLPlatform<org.eclipse.persistence.internal.oxm.XMLUnmarshaller> platform = new SAXPlatform(); |
| platform.getConversionManager().setLoader(classLoader); |
| project.setLogin(new XMLLogin(platform)); |
| } |
| } |
| |
| private void setupDocumentPreservationPolicy(DatabaseSession session) { |
| XMLLogin login = (XMLLogin) session.getDatasourceLogin(); |
| if (login.getDocumentPreservationPolicy() == null) { |
| Iterator<ClassDescriptor> iterator = session.getProject().getOrderedDescriptors().iterator(); |
| while (iterator.hasNext()) { |
| Descriptor xmlDescriptor = (Descriptor) iterator.next(); |
| if (xmlDescriptor.shouldPreserveDocument()) { |
| login.setDocumentPreservationPolicy(new DescriptorLevelDocumentPreservationPolicy()); |
| break; |
| } |
| } |
| } |
| if (login.getDocumentPreservationPolicy() == null) { |
| login.setDocumentPreservationPolicy(new NoDocumentPreservationPolicy()); |
| } |
| |
| login.getDocumentPreservationPolicy().initialize(context); |
| |
| if (login.getDocumentPreservationPolicy().shouldPreserveDocument() && !hasDocumentPreservation) { |
| hasDocumentPreservation = true; |
| } |
| } |
| |
| } |
| |
| @Override |
| protected XMLField createField(String path) { |
| if(null == path) { |
| return new XMLField(); |
| } |
| return new XMLField(path); |
| } |
| |
| /** |
| * Returns descriptors from all sessions. |
| * |
| * @return descriptors from all sessions |
| */ |
| public List<Descriptor> getDescriptors() { |
| List<Descriptor> descriptors = new ArrayList<>(); |
| List<Session> sessions = getSessions(); |
| for (Session session : sessions) { |
| List<Descriptor> orderedDescriptors = (List) session.getProject().getOrderedDescriptors(); |
| for (Descriptor xDesc : orderedDescriptors) { |
| descriptors.add(xDesc); |
| } |
| } |
| return descriptors; |
| } |
| |
| /** |
| * Returns conversion manager from session datasource platform. |
| * |
| * @return conversion manager |
| */ |
| public ConversionManager getOxmConversionManager() { |
| return (org.eclipse.persistence.internal.oxm.ConversionManager) getSession().getDatasourcePlatform().getConversionManager(); |
| } |
| |
| /** |
| * Returns descriptor for given object. |
| * |
| * @return descriptor for given object |
| */ |
| public Descriptor getDescriptorForObject(Object object) { |
| Session session = getSession(object); |
| if (null != session && null != session.getDescriptor(object)) { |
| return (Descriptor)session.getDescriptor(object); |
| } |
| |
| return null; |
| } |
| |
| private static ClassLoader privilegedGetClassLoaderForClass(final Class clazz) { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| return AccessController.doPrivileged(new PrivilegedGetClassLoaderForClass(clazz)); |
| } catch (PrivilegedActionException ex) { |
| throw (RuntimeException) ex.getCause(); |
| } |
| } |
| return PrivilegedAccessHelper.getClassLoaderForClass(clazz); |
| } |
| |
| } |