blob: 2537d03fc67ad00a3c15dda7a4e2f354a19eb3f9 [file] [log] [blame]
/*
* 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 ormDescriptors = ormSession.getDescriptors().values().iterator();
while(ormDescriptors.hasNext()) {
ClassDescriptor ormDescriptor = (ClassDescriptor)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 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 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 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 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);
}
}