| /* |
| * Copyright (c) 2011, 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: |
| // dclarke/tware - initial |
| // 2014-09-01-2.6.0 Dmitry Kornilov |
| // - JPARS 2.0 related changes |
| package org.eclipse.persistence.jpa.rs; |
| |
| import java.beans.BeanInfo; |
| import java.beans.IntrospectionException; |
| import java.beans.Introspector; |
| import java.beans.PropertyDescriptor; |
| import java.io.BufferedInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URI; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import jakarta.persistence.EntityManager; |
| import jakarta.persistence.EntityManagerFactory; |
| import jakarta.persistence.Query; |
| import jakarta.persistence.RollbackException; |
| import jakarta.persistence.spi.PersistenceUnitInfo; |
| import jakarta.ws.rs.core.MediaType; |
| import jakarta.xml.bind.JAXBElement; |
| import jakarta.xml.bind.JAXBException; |
| import jakarta.xml.bind.Marshaller; |
| import jakarta.xml.bind.Unmarshaller; |
| import jakarta.xml.bind.ValidationEvent; |
| import jakarta.xml.bind.ValidationEventHandler; |
| import jakarta.xml.bind.annotation.adapters.XmlAdapter; |
| import javax.xml.stream.XMLOutputFactory; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.transform.stream.StreamSource; |
| |
| import org.eclipse.persistence.config.PersistenceUnitProperties; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.descriptors.FetchGroupManager; |
| import org.eclipse.persistence.dynamic.DynamicEntity; |
| import org.eclipse.persistence.dynamic.DynamicType; |
| import org.eclipse.persistence.eis.mappings.EISCompositeCollectionMapping; |
| import org.eclipse.persistence.internal.helper.ConversionManager; |
| import org.eclipse.persistence.internal.jaxb.SessionEventListener; |
| import org.eclipse.persistence.internal.jpa.EJBQueryImpl; |
| import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl; |
| import org.eclipse.persistence.internal.jpa.rs.weaving.PersistenceWeavedRest; |
| import org.eclipse.persistence.internal.jpa.rs.weaving.RelationshipInfo; |
| import org.eclipse.persistence.internal.jpa.rs.weaving.RestAdapterClassWriter; |
| import org.eclipse.persistence.internal.jpa.rs.weaving.RestCollectionAdapterClassWriter; |
| import org.eclipse.persistence.internal.jpa.rs.weaving.RestReferenceAdapterV2ClassWriter; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredFields; |
| import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.jaxb.JAXBContext; |
| import org.eclipse.persistence.jaxb.JAXBContextProperties; |
| import org.eclipse.persistence.jaxb.MarshallerProperties; |
| import org.eclipse.persistence.jaxb.ObjectGraph; |
| import org.eclipse.persistence.jaxb.UnmarshallerProperties; |
| import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory; |
| import org.eclipse.persistence.jaxb.metadata.MetadataSource; |
| import org.eclipse.persistence.jpa.JpaHelper; |
| import org.eclipse.persistence.jpa.PersistenceProvider; |
| import org.eclipse.persistence.jpa.dynamic.JPADynamicHelper; |
| import org.eclipse.persistence.jpa.rs.annotations.RestPageableQueries; |
| import org.eclipse.persistence.jpa.rs.annotations.RestPageableQuery; |
| import org.eclipse.persistence.jpa.rs.exceptions.JPARSException; |
| import org.eclipse.persistence.jpa.rs.features.FeatureSet; |
| import org.eclipse.persistence.jpa.rs.features.ServiceVersion; |
| import org.eclipse.persistence.jpa.rs.features.fieldsfiltering.FieldsFilter; |
| import org.eclipse.persistence.jpa.rs.util.CollectionWrapperBuilder; |
| import org.eclipse.persistence.jpa.rs.util.IdHelper; |
| import org.eclipse.persistence.jpa.rs.util.JPARSLogger; |
| import org.eclipse.persistence.jpa.rs.util.JTATransactionWrapper; |
| import org.eclipse.persistence.jpa.rs.util.ObjectGraphBuilder; |
| import org.eclipse.persistence.jpa.rs.util.ResourceLocalTransactionWrapper; |
| import org.eclipse.persistence.jpa.rs.util.TransactionWrapper; |
| import org.eclipse.persistence.jpa.rs.util.list.ReadAllQueryResultCollection; |
| import org.eclipse.persistence.jpa.rs.util.list.ReportQueryResultCollection; |
| import org.eclipse.persistence.jpa.rs.util.list.ReportQueryResultList; |
| import org.eclipse.persistence.jpa.rs.util.list.ReportQueryResultListItem; |
| import org.eclipse.persistence.jpa.rs.util.list.SingleResultQueryList; |
| import org.eclipse.persistence.jpa.rs.util.xmladapters.LinkAdapter; |
| import org.eclipse.persistence.jpa.rs.util.xmladapters.RelationshipLinkAdapter; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.ForeignReferenceMapping; |
| import org.eclipse.persistence.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.oxm.mappings.XMLInverseReferenceMapping; |
| import org.eclipse.persistence.queries.DatabaseQuery; |
| import org.eclipse.persistence.queries.FetchGroup; |
| import org.eclipse.persistence.queries.FetchGroupTracker; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.sessions.UnitOfWork; |
| |
| /** |
| * A wrapper around the JPA and JAXB artifacts used to persist an application. |
| * |
| * A PersistenceContext provides the capability of using the same persistence unit in JPA to |
| * to interact with a Database or other JPA-capable data source and in JAXB to interact with either |
| * XML or JSON. |
| * |
| * A PersistenceContext can wrap either an existing persistence unit (EntityManagerFactory), or it can be used to bootstrap a |
| * fully dynamic persistence unit. |
| * |
| * @author douglas.clarke, tom.ware |
| */ |
| public class PersistenceContext { |
| public static final String JPARS_CONTEXT = "eclipselink.jpars.context"; |
| public static final String CLASS_NAME = PersistenceContext.class.getName(); |
| public static final String SESSION_VERSION_PROPERTY = "jaxb.context.version"; |
| |
| protected List<XmlAdapter<?, ?>> adapters = null; |
| |
| /** |
| * The name of the persistence context is used to look it up. By default it will be the |
| * persistence unit name of the JPA persistence unit. |
| */ |
| protected String name = null; |
| |
| /** The EntityManagerFactory used to interact using JPA **/ |
| protected EntityManagerFactory emf; |
| |
| /** The JAXBConext used to produce JSON or XML **/ |
| protected JAXBContext jaxbContext = null; |
| |
| /** The URI of the Persistence context. This is used to build Links in JSON and XML **/ |
| protected URI baseURI = null; |
| |
| private SessionLog sessionLog = null; |
| |
| protected TransactionWrapper transaction = null; |
| |
| private Boolean weavingEnabled = null; |
| |
| private ServiceVersion version = ServiceVersion.NO_VERSION; |
| |
| /** Builder for collection proxies used in JPARS 2.0. **/ |
| private CollectionWrapperBuilder collectionWrapperBuilder; |
| |
| /** |
| * JPARS pageable queries map. |
| * Key: named query name |
| * Value: corresponding RestPageableQuery annotation |
| */ |
| private Map<String, RestPageableQuery> pageableQueries; |
| |
| protected PersistenceContext() { |
| } |
| |
| /** |
| * Instantiates a new persistence context. |
| * |
| * @param emfName the emf name |
| * @param emf the emf |
| * @param defaultURI the default uri |
| */ |
| public PersistenceContext(String emfName, EntityManagerFactoryImpl emf, URI defaultURI) { |
| super(); |
| init(emfName, emf, defaultURI, ServiceVersion.NO_VERSION); |
| } |
| |
| /** |
| * Instantiates a new persistence context. |
| * |
| * @param emfName the emf name |
| * @param emf the emf |
| * @param defaultURI the default uri |
| * @param version REST service version |
| */ |
| public PersistenceContext(String emfName, EntityManagerFactoryImpl emf, URI defaultURI, ServiceVersion version) { |
| super(); |
| init(emfName, emf, defaultURI, version); |
| } |
| |
| private void init(String emfName, EntityManagerFactoryImpl emf, URI defaultURI, ServiceVersion version) { |
| this.emf = emf; |
| this.name = emfName; |
| this.baseURI = defaultURI; |
| this.sessionLog = emf.getServerSession().getSessionLog(); |
| |
| if (version != null) { |
| this.version = version; |
| } else { |
| this.version = ServiceVersion.NO_VERSION; |
| } |
| |
| if (getServerSession().hasExternalTransactionController()) { |
| transaction = new JTATransactionWrapper(); |
| } else { |
| transaction = new ResourceLocalTransactionWrapper(); |
| } |
| |
| try { |
| this.jaxbContext = createDynamicJAXBContext(emf.getDatabaseSession()); |
| } catch (JAXBException jaxbe) { |
| JPARSLogger.exception(getSessionLog(), "exception_creating_jaxb_context", new Object[] { emfName, jaxbe.toString() }, jaxbe); |
| emf.close(); |
| } catch (IOException e) { |
| JPARSLogger.exception(getSessionLog(), "exception_creating_jaxb_context", new Object[] { emfName, e.toString() }, e); |
| emf.close(); |
| } |
| } |
| |
| /** |
| * Checks if is weaving enabled. |
| * |
| * @return true, if is weaving enabled |
| */ |
| public boolean isWeavingEnabled() { |
| if (this.weavingEnabled == null) { |
| this.weavingEnabled = getWeavingProperty(); |
| } |
| return this.weavingEnabled; |
| } |
| |
| /** |
| * Gets the version as it appears in URI. |
| * |
| * @return The version. |
| */ |
| public String getVersion() { |
| return version.getCode(); |
| } |
| |
| /** |
| * Gets JPARS version. |
| * |
| * @return JPARS version. |
| */ |
| public ServiceVersion getServiceVersion() { |
| return version; |
| } |
| |
| /** |
| * This method is used to help construct a JAXBContext from an existing EntityManagerFactory. |
| * |
| * For each package in the EntityManagerFactory, a MetadataSource that is capable of building a JAXBContext |
| * that creates the same mappings in JAXB is created. These MetadataSources are used to constuct the JAXContext |
| * that is used for JSON and XML translation. |
| */ |
| protected void addDynamicXMLMetadataSources(List<Object> metadataSources, AbstractSession session) { |
| Set<String> packages = new HashSet<String>(); |
| for (Class<?> descriptorClass : session.getDescriptors().keySet()) { |
| String packageName = ""; |
| int lastDotIndex = descriptorClass.getName().lastIndexOf('.'); |
| if (lastDotIndex > 0) { |
| packageName = descriptorClass.getName().substring(0, lastDotIndex); |
| } |
| if (!packages.contains(packageName)) { |
| packages.add(packageName); |
| } |
| } |
| |
| for (String packageName : packages) { |
| metadataSources.add(getSupportedFeatureSet().getDynamicMetadataSource(session, packageName)); |
| } |
| } |
| |
| /** |
| * A part of the facade over the JPA API. |
| * Persist an entity in JPA and commit. |
| */ |
| public void create(Map<String, String> tenantId, Object entity) throws Exception { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| try { |
| transaction.beginTransaction(em); |
| em.persist(entity); |
| transaction.commitTransaction(em); |
| } catch (RollbackException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| transaction.rollbackTransaction(em); |
| throw ex; |
| } finally { |
| em.close(); |
| } |
| } |
| |
| /** |
| * Create a JAXBContext based on the EntityManagerFactory for this PersistenceContext. |
| */ |
| protected JAXBContext createDynamicJAXBContext(AbstractSession session) throws JAXBException, IOException { |
| final ServiceVersion cachedContextVersion = (ServiceVersion) session.getProperty(SESSION_VERSION_PROPERTY); |
| final JAXBContext cachedContext = (JAXBContext) session.getProperty(JAXBContext.class.getName()); |
| if (cachedContext != null && cachedContextVersion != null && cachedContextVersion == version) { |
| return cachedContext; |
| } |
| |
| final Map<String, Object> properties = createJAXBProperties(session); |
| final ClassLoader cl = session.getDatasourcePlatform().getConversionManager().getLoader(); |
| final JAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(cl, properties); |
| |
| session.setProperty(SESSION_VERSION_PROPERTY, version); |
| session.setProperty(JAXBContext.class.getName(), jaxbContext); |
| |
| return jaxbContext; |
| } |
| |
| /** |
| * A part of the facade over the JPA API. |
| * Create an EntityManagerFactory using the given PersistenceUnitInfo and properties. |
| */ |
| protected EntityManagerFactoryImpl createEntityManagerFactory(PersistenceUnitInfo info, Map<String, ?> properties) { |
| PersistenceProvider provider = new PersistenceProvider(); |
| EntityManagerFactory emf = provider.createContainerEntityManagerFactory(info, properties); |
| return (EntityManagerFactoryImpl) emf; |
| } |
| |
| /** |
| * A part of the facade over the JPA API |
| * Create an EntityManager from the EntityManagerFactory wrapped by this persistence context |
| */ |
| protected EntityManager createEntityManager(String tenantId) { |
| return getEmf().createEntityManager(); |
| } |
| |
| /** |
| * Build the set of properties used to create the JAXBContext based on the EntityManagerFactory that |
| * this PersistenceContext wraps |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| protected Map<String, Object> createJAXBProperties(AbstractSession session) throws IOException { |
| Map<String, Object> properties = new HashMap<String, Object>(1); |
| List<Object> metadataLocations = new ArrayList<Object>(); |
| |
| addDynamicXMLMetadataSources(metadataLocations, session); |
| |
| String oxmLocation = (String) emf.getProperties().get("eclipselink.jpa-rs.oxm"); |
| if (oxmLocation != null) { |
| metadataLocations.add(oxmLocation); |
| } |
| |
| Object passedOXMLocations = emf.getProperties().get(JAXBContextProperties.OXM_METADATA_SOURCE); |
| if (passedOXMLocations != null) { |
| if (passedOXMLocations instanceof Collection) { |
| metadataLocations.addAll((Collection) passedOXMLocations); |
| } else { |
| metadataLocations.add(passedOXMLocations); |
| } |
| } |
| |
| // Add static metadata sources specific to current version |
| for (MetadataSource metadataSource : getSupportedFeatureSet().getMetadataSources()) { |
| metadataLocations.add(metadataSource); |
| } |
| |
| properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, metadataLocations); |
| |
| SessionEventListener sessionEventListener = getSupportedFeatureSet().getSessionEventListener(session); |
| if (sessionEventListener != null) { |
| properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener); |
| } |
| |
| // Bug 410095 - JSON_WRAPPER_AS_ARRAY_NAME property doesn't work when jaxb context is created using DynamicJAXBContextFactory |
| //properties.put(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); |
| |
| return properties; |
| } |
| |
| /** |
| * A part of the facade over the JPA API |
| * Delete the given entity in JPA and commit the changes |
| */ |
| public void delete(Map<String, String> tenantId, String type, Object id) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| |
| try { |
| transaction.beginTransaction(em); |
| Object entity = em.find(getClass(type), id); |
| if (entity != null) { |
| em.remove(entity); |
| } |
| transaction.commitTransaction(em); |
| } catch (RollbackException ex) { |
| throw JPARSException.exceptionOccurred(ex); |
| } catch (Exception ex) { |
| transaction.rollbackTransaction(em); |
| throw JPARSException.exceptionOccurred(ex); |
| } finally { |
| em.close(); |
| } |
| } |
| |
| /** |
| * Does exist. |
| * |
| * @param tenantId the tenant id |
| * @param entity the entity |
| * @return true, if successful |
| */ |
| public boolean doesExist(Map<String, String> tenantId, Object entity) { |
| DatabaseSession session = JpaHelper.getDatabaseSession(getEmf()); |
| return session.doesObjectExist(entity); |
| } |
| |
| /** |
| * Finalize. |
| */ |
| @Override |
| protected void finalize() throws Throwable { |
| emf.close(); |
| super.finalize(); |
| } |
| |
| /** |
| * A part of the facade over the JPA API |
| * Find an entity with the given name and id in JPA |
| */ |
| public Object find(String entityName, Object id) { |
| return find(null, entityName, id); |
| } |
| |
| /** |
| * A part of the facade over the JPA API |
| * Find an entity with the given name and id in JPA |
| */ |
| public Object find(Map<String, String> tenantId, String entityName, Object id) { |
| return find(tenantId, entityName, id, null); |
| } |
| |
| /** |
| * A part of the facade over the JPA API |
| * Find an entity with the given name and id in JPA |
| * @param properties - query hints used on the find |
| */ |
| public Object find(Map<String, String> tenantId, String entityName, Object id, Map<String, Object> properties) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| |
| try { |
| return em.find(getClass(entityName), id, properties); |
| } finally { |
| em.close(); |
| } |
| } |
| |
| /** |
| * Update or add attribute. |
| * |
| * @param tenantId the tenant id |
| * @param entityName the entity name |
| * @param id the id |
| * @param properties the properties |
| * @param attribute the attribute |
| * @param attributeValue the attribute value |
| * @param partner the partner |
| * @return the object |
| */ |
| public Object updateOrAddAttribute(Map<String, String> tenantId, String entityName, Object id, Map<String, Object> properties, String attribute, Object attributeValue, String partner) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| |
| try { |
| ClassDescriptor descriptor = getServerSession().getClassDescriptor(getClass(entityName)); |
| DatabaseMapping mapping = descriptor.getMappingForAttributeName(attribute); |
| Object object; |
| if (mapping == null) { |
| return null; |
| } else if (mapping.isObjectReferenceMapping() || mapping.isCollectionMapping()) { |
| DatabaseMapping partnerMapping = null; |
| if (partner != null) { |
| ClassDescriptor referenceDescriptor = mapping.getReferenceDescriptor(); |
| partnerMapping = referenceDescriptor.getMappingForAttributeName(partner); |
| if (partnerMapping == null) { |
| return null; |
| } |
| } |
| transaction.beginTransaction(em); |
| try { |
| object = em.find(getClass(entityName), id, properties); |
| if (object == null) { |
| return null; |
| } |
| attributeValue = em.merge(attributeValue); |
| setMappingValueInObject(object, attributeValue, mapping, partnerMapping); |
| transaction.commitTransaction(em); |
| } catch (RollbackException e) { |
| JPARSLogger.exception(getSessionLog(), "exception_while_updating_attribute", new Object[] { entityName, getName() }, e); |
| return null; |
| } catch (Exception e) { |
| JPARSLogger.exception(getSessionLog(), "exception_while_updating_attribute", new Object[] { entityName, getName() }, e); |
| transaction.rollbackTransaction(em); |
| return null; |
| } |
| } else { |
| return null; |
| } |
| return object; |
| } finally { |
| em.close(); |
| } |
| } |
| |
| /** |
| * Removes the attribute. |
| * |
| * @param tenantId the tenant id |
| * @param entityName the entity name |
| * @param id the id |
| * @param attribute the attribute |
| * @param partner the partner |
| * @return the object |
| * |
| */ |
| @SuppressWarnings({"rawtypes" }) |
| public Object removeAttribute(Map<String, String> tenantId, String entityName, Object id, String attribute, String listItemId, Object entity, String partner) |
| { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| String fieldName = null; |
| |
| try { |
| Class<?> clazz = getClass(entityName); |
| ClassDescriptor descriptor = getServerSession().getClassDescriptor(clazz); |
| DatabaseMapping mapping = descriptor.getMappingForAttributeName(attribute); |
| if (mapping == null) { |
| return null; |
| } else if (mapping.isObjectReferenceMapping() || mapping.isCollectionMapping()) { |
| DatabaseMapping partnerMapping = null; |
| Object originalAttributeValue = null; |
| ClassDescriptor referenceDescriptor = mapping.getReferenceDescriptor(); |
| if (partner != null) { |
| partnerMapping = referenceDescriptor.getMappingForAttributeName(partner); |
| if (partnerMapping == null) { |
| return null; |
| } |
| } |
| Field[] fields; |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| fields = AccessController.doPrivileged(new PrivilegedGetDeclaredFields(clazz)); |
| } else { |
| fields = PrivilegedAccessHelper.getDeclaredFields(clazz); |
| } |
| |
| for (Field field : fields) { |
| fieldName = field.getName(); |
| if (fieldName.equals(attribute)) { |
| try { |
| // call clear on this collection |
| Object attributeValue = getAttribute(entity, attribute); |
| originalAttributeValue = attributeValue; |
| if (attributeValue instanceof Collection) { |
| if (listItemId == null) { |
| // no collection member specified in request (listItemId=null) remove entire collection |
| ((Collection) attributeValue).clear(); |
| } else { |
| Object realListItemId = IdHelper.buildId(this, referenceDescriptor.getAlias(), listItemId); |
| Object member = this.find(referenceDescriptor.getAlias(), realListItemId); |
| ((Collection) attributeValue).remove(member); |
| } |
| } |
| break; |
| } catch (Exception e) { |
| e.printStackTrace(); |
| return null; |
| } |
| } |
| } |
| |
| transaction.beginTransaction(em); |
| entity = em.merge(entity); |
| removeMappingValueFromObject(entity, originalAttributeValue, mapping, partnerMapping); |
| transaction.commitTransaction(em); |
| return entity; |
| } |
| return null; |
| } catch (Exception e) { |
| JPARSLogger.exception(getSessionLog(), "exception_while_removing_attribute", new Object[] { fieldName, entityName, getName() }, e); |
| transaction.rollbackTransaction(em); |
| return null; |
| } finally { |
| em.close(); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Object getAttribute(Object entity, String propertyName) { |
| try { |
| BeanInfo info = Introspector.getBeanInfo(entity.getClass(), Object.class); |
| PropertyDescriptor[] props = info.getPropertyDescriptors(); |
| for (PropertyDescriptor pd : props) { |
| String name = pd.getName(); |
| if (propertyName.equals(name)) { |
| Method getter = pd.getReadMethod(); |
| Object value; |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| value = AccessController.doPrivileged(new PrivilegedMethodInvoker(getter, entity)); |
| } else { |
| value = PrivilegedAccessHelper.invokeMethod(getter, entity); |
| } |
| return value; |
| } |
| } |
| } catch (IntrospectionException | PrivilegedActionException | IllegalAccessException | InvocationTargetException ex) { |
| return null; |
| } |
| return null; |
| } |
| |
| @SuppressWarnings("rawtypes") |
| protected void removeMappingValueFromObject(Object object, Object attributeValue, DatabaseMapping mapping, DatabaseMapping partner) { |
| if (mapping.isObjectReferenceMapping()) { |
| Object currentValue = mapping.getRealAttributeValueFromObject(object, (AbstractSession) getServerSession()); |
| if (currentValue.equals(attributeValue)) { |
| ((ObjectReferenceMapping) mapping).getIndirectionPolicy().setRealAttributeValueInObject(object, null, true); |
| if (partner != null) { |
| removeMappingValueFromObject(attributeValue, object, partner, null); |
| } |
| } |
| } else if (mapping.isCollectionMapping()) { |
| boolean removed = ((Collection) mapping.getRealAttributeValueFromObject(object, (AbstractSession) getServerSession())).remove(attributeValue); |
| if (removed && partner != null) { |
| removeMappingValueFromObject(attributeValue, object, partner, null); |
| } |
| } |
| } |
| |
| /** |
| * Gets the base uri. |
| * |
| * @return the base uri |
| */ |
| public URI getBaseURI() { |
| return baseURI; |
| } |
| |
| /** |
| * Look-up the given entity name in the EntityManagerFactory and return the class |
| * is describes |
| */ |
| public Class<?> getClass(String entityName) { |
| ClassDescriptor descriptor = getDescriptor(entityName); |
| if (descriptor == null) { |
| return null; |
| } |
| return descriptor.getJavaClass(); |
| } |
| |
| /** |
| * Gets the jpa server session. |
| * |
| * @return the jpa server session |
| */ |
| public DatabaseSession getServerSession() { |
| // Fix for bug 390786 - JPA-RS: ClassCastException retrieving metadata for Composite Persistence Unit |
| return JpaHelper.getDatabaseSession(emf); |
| } |
| |
| /** |
| * Gets the client session. |
| * |
| * @param em the em |
| * @return the client session |
| */ |
| public AbstractSession getClientSession(EntityManager em) { |
| UnitOfWork uow = JpaHelper.getEntityManager(em).getUnitOfWork(); |
| return (AbstractSession) uow; |
| } |
| |
| /** |
| * Lookup the descriptor for the given entity name. |
| * This method will look first in the EntityManagerFactory wrapped by this persistence context |
| * and return that descriptor. If one does not exist, it search the JAXBContext and return |
| * a descriptor from there. |
| */ |
| public ClassDescriptor getDescriptor(String entityName) { |
| DatabaseSession session = getServerSession(); |
| ClassDescriptor descriptor = session.getDescriptorForAlias(entityName); |
| if (descriptor == null) { |
| for (Object ajaxBSession : getJAXBContext().getXMLContext().getSessions()) { |
| descriptor = ((Session) ajaxBSession).getClassDescriptorForAlias(entityName); |
| if (descriptor != null) { |
| break; |
| } |
| } |
| } |
| return descriptor; |
| } |
| |
| /** |
| * Gets the descriptor for class. |
| * |
| * @param clazz the clazz |
| * @return the descriptor for class |
| */ |
| @SuppressWarnings("rawtypes") |
| public ClassDescriptor getDescriptorForClass(Class clazz) { |
| DatabaseSession session = getServerSession(); |
| ClassDescriptor descriptor = session.getDescriptor(clazz); |
| if (descriptor == null) { |
| return getJAXBDescriptorForClass(clazz); |
| } |
| return descriptor; |
| } |
| |
| /** |
| * Gets the jAXB descriptor for class. |
| * |
| * @param clazz the clazz |
| * @return the jAXB descriptor for class |
| */ |
| @SuppressWarnings("rawtypes") |
| public ClassDescriptor getJAXBDescriptorForClass(Class clazz) { |
| ClassDescriptor descriptor = null; |
| for (Object ajaxBSession : getJAXBContext().getXMLContext().getSessions()) { |
| descriptor = ((Session) ajaxBSession).getClassDescriptor(clazz); |
| if (descriptor != null) { |
| break; |
| } |
| } |
| return descriptor; |
| } |
| |
| /** |
| * Gets the emf. |
| * |
| * @return the emf |
| */ |
| public EntityManagerFactory getEmf() { |
| return emf; |
| } |
| |
| /** |
| * Gets the jAXB context. |
| * |
| * @return the jAXB context |
| */ |
| public JAXBContext getJAXBContext() { |
| return jaxbContext; |
| } |
| |
| /** |
| * Gets the name. |
| * |
| * @return the name |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| public SessionLog getSessionLog() { |
| return sessionLog; |
| } |
| |
| /** |
| * A part of the facade over the JPA API |
| * Call jpa merge on the given object and commit |
| * If the passed object is a list, we will iterate through the |
| * list and merge each member |
| */ |
| @SuppressWarnings("rawtypes") |
| public Object merge(Map<String, String> tenantId, Object entity) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| Object mergedEntity; |
| try { |
| transaction.beginTransaction(em); |
| if (entity instanceof List) { |
| List<Object> mergeList = new ArrayList<Object>(); |
| for (Object o : (List) entity) { |
| mergeList.add(em.merge(o)); |
| } |
| mergedEntity = mergeList; |
| } else { |
| mergedEntity = em.merge(entity); |
| } |
| transaction.commitTransaction(em); |
| return mergedEntity; |
| } catch (RollbackException ex) { |
| throw JPARSException.exceptionOccurred(ex); |
| } catch (Exception ex) { |
| transaction.rollbackTransaction(em); |
| throw JPARSException.exceptionOccurred(ex); |
| } finally { |
| em.close(); |
| } |
| } |
| |
| /** |
| * A convenience method to create a new dynamic entity of the given type |
| */ |
| public DynamicEntity newEntity(String type) { |
| return newEntity(null, type); |
| } |
| |
| /** |
| * A convenience method to create a new dynamic entity of the given type |
| */ |
| public DynamicEntity newEntity(Map<String, String> tenantId, String type) { |
| JPADynamicHelper helper = new JPADynamicHelper(getEmf()); |
| DynamicEntity entity; |
| try { |
| entity = helper.newDynamicEntity(type); |
| } catch (IllegalArgumentException e) { |
| ClassDescriptor descriptor = getDescriptor(type); |
| if (descriptor != null) { |
| DynamicType jaxbType = (DynamicType) descriptor.getProperty(DynamicType.DESCRIPTOR_PROPERTY); |
| if (jaxbType != null) { |
| return jaxbType.newDynamicEntity(); |
| } |
| } |
| JPARSLogger.exception(getSessionLog(), "exception_thrown_while_creating_dynamic_entity", new Object[] { type }, e); |
| throw e; |
| } |
| return entity; |
| } |
| |
| /** |
| * Query execute update. |
| * |
| * @param tenantId the tenant id |
| * @param name the name |
| * @param parameters the parameters |
| * @param hints the hints |
| * @return the int |
| */ |
| public int queryExecuteUpdate(Map<String, String> tenantId, String name, Map<?, ?> parameters, Map<String, ?> hints) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| try { |
| Query query = constructQuery(em, name, parameters, hints); |
| transaction.beginTransaction(em); |
| int result = query.executeUpdate(); |
| transaction.commitTransaction(em); |
| return result; |
| } catch (RollbackException ex) { |
| throw JPARSException.exceptionOccurred(ex); |
| } catch (Exception ex) { |
| transaction.rollbackTransaction(em); |
| throw JPARSException.exceptionOccurred(ex); |
| } finally { |
| em.close(); |
| } |
| } |
| |
| /** |
| * Query multiple results. |
| * |
| * @param tenantId the tenant id |
| * @param name the name |
| * @param parameters the parameters |
| * @param hints the hints |
| * @return the list |
| */ |
| @SuppressWarnings("rawtypes") |
| public List queryMultipleResults(Map<String, String> tenantId, String name, Map<?, ?> parameters, Map<String, ?> hints) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| try { |
| Query query = constructQuery(em, name, parameters, hints); |
| return query.getResultList(); |
| } finally { |
| em.close(); |
| } |
| } |
| |
| @SuppressWarnings("rawtypes") |
| protected Query constructQuery(EntityManager em, String name, Map<?, ?> parameters, Map<String, ?> hints) { |
| Query query = em.createNamedQuery(name); |
| DatabaseQuery dbQuery = ((EJBQueryImpl<?>) query).getDatabaseQuery(); |
| if (parameters != null) { |
| Iterator<?> i = parameters.entrySet().iterator(); |
| while (i.hasNext()) { |
| Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next(); |
| String key = (String) entry.getKey(); |
| Class parameterClass = null; |
| int index = dbQuery.getArguments().indexOf(key); |
| if (index >= 0) { |
| parameterClass = dbQuery.getArgumentTypes().get(index); |
| } |
| Object parameter = entry.getValue(); |
| if (parameterClass != null) { |
| parameter = ConversionManager.getDefaultManager().convertObject(parameter, parameterClass); |
| } |
| query.setParameter(key, parameter); |
| } |
| } |
| if (hints != null) { |
| for (Map.Entry<String, ?> entry : hints.entrySet()) { |
| query.setHint(entry.getKey(), entry.getValue()); |
| } |
| } |
| return query; |
| } |
| |
| /** |
| * Builds the query. |
| * |
| * @param tenantId the tenant id |
| * @param name the name |
| * @param parameters the parameters |
| * @param hints the hints |
| * @return the query |
| */ |
| @SuppressWarnings("rawtypes") |
| public Query buildQuery(Map<String, String> tenantId, String name, Map<?, ?> parameters, Map<String, ?> hints) { |
| EntityManager em = getEmf().createEntityManager(tenantId); |
| Query query = em.createNamedQuery(name); |
| DatabaseQuery dbQuery = ((EJBQueryImpl<?>) query).getDatabaseQuery(); |
| if (parameters != null) { |
| Iterator<?> i = parameters.entrySet().iterator(); |
| while (i.hasNext()) { |
| Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next(); |
| String key = (String) entry.getKey(); |
| Class parameterClass = null; |
| int index = dbQuery.getArguments().indexOf(key); |
| if (index >= 0) { |
| parameterClass = dbQuery.getArgumentTypes().get(index); |
| } |
| Object parameter = entry.getValue(); |
| if (parameterClass != null) { |
| parameter = ConversionManager.getDefaultManager().convertObject(parameter, parameterClass); |
| } |
| query.setParameter(key, parameter); |
| } |
| } |
| if (hints != null) { |
| for (Map.Entry<String, ?> entry : hints.entrySet()) { |
| query.setHint(entry.getKey(), entry.getValue()); |
| } |
| } |
| return query; |
| } |
| |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| protected void setMappingValueInObject(Object object, Object attributeValue, DatabaseMapping mapping, DatabaseMapping partner) { |
| if (mapping.isObjectReferenceMapping()) { |
| ((ObjectReferenceMapping) mapping).getIndirectionPolicy().setRealAttributeValueInObject(object, attributeValue, true); |
| if (partner != null) { |
| setMappingValueInObject(attributeValue, object, partner, null); |
| } |
| } else if (mapping.isCollectionMapping()) { |
| ((Collection) mapping.getAttributeValueFromObject(object)).add(attributeValue); |
| if (partner != null) { |
| setMappingValueInObject(attributeValue, object, partner, null); |
| } |
| } |
| } |
| |
| /** |
| * Stop the current application instance |
| */ |
| public void stop() { |
| if (emf != null && emf.isOpen()) { |
| emf.close(); |
| } |
| this.emf = null; |
| this.jaxbContext = null; |
| } |
| |
| /** |
| * To string. |
| * |
| * @return the string |
| */ |
| @Override |
| public String toString() { |
| return "PersistenceContext(name:" + getName() + ", version:" + getVersion() + ", identityHashCode:" + System.identityHashCode(this) + ")"; |
| } |
| |
| /** |
| * Unmarshal entity. |
| * |
| * @param type the type of the entity to unmarshal |
| * @param acceptedMediaType the accepted media type |
| * @param in the input stream to unmarshal |
| * @return the object |
| * @throws JAXBException the JAXB exception |
| */ |
| public Object unmarshalEntity(String type, MediaType acceptedMediaType, InputStream in) throws JAXBException { |
| if (JPARSLogger.isLoggableFinest(getSessionLog())) { |
| in = in.markSupported() ? in : new BufferedInputStream(in); |
| // TODO: Make readlimit configurable. Some http servers allow http post size to be unlimited. |
| // If this is the case and if an application is sending huge post requests while jpars log |
| // level configured to finest, this readlimit might not be sufficient. |
| in.mark(52428800); // (~50MB) |
| JPARSLogger.entering(getSessionLog(), CLASS_NAME, "unmarshalEntity", in); |
| } |
| Object unmarshalled = unmarshal(getClass(type), acceptedMediaType, in); |
| JPARSLogger.exiting(getSessionLog(), CLASS_NAME, "unmarshalEntity", new Object[] { unmarshalled.getClass().getName(), unmarshalled }); |
| return unmarshalled; |
| } |
| |
| /** |
| * Unmarshal. |
| * |
| * @param type the type of the entity to unmarshal |
| * @param acceptedMediaType the accepted media type |
| * @param in the input stream to unmarshal |
| * @return the object |
| * @throws JAXBException the JAXB exception |
| */ |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public Object unmarshal(Class type, MediaType acceptedMediaType, InputStream in) throws JAXBException { |
| Unmarshaller unmarshaller = getJAXBContext().createUnmarshaller(); |
| unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, Boolean.FALSE); |
| unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, acceptedMediaType.toString()); |
| unmarshaller.setAdapter(new LinkAdapter(getBaseURI().toString(), this)); |
| unmarshaller.setEventHandler(new ValidationEventHandler() { |
| @Override |
| /* |
| * ReferenceAdaptor unmarshal throws exception if the object referred by a link |
| * doesn't exist, and this handler is required to interrupt the unmarshal |
| * operation under this condition. |
| * (non-Javadoc) @see jakarta.xml.bind.ValidationEventHandler#handleEvent(jakarta.xml.bind.ValidationEvent) |
| * |
| */ |
| public boolean handleEvent(ValidationEvent event) { |
| if (event.getSeverity() != ValidationEvent.WARNING) { |
| // ValidationEventLocator eventLocator = event.getLocator(); |
| // Throwable throwable = event.getLinkedException(); |
| // nothing is really useful to check for us in eventLocator |
| // and linked exception, just return false; |
| return false; |
| } |
| return true; |
| } |
| }); |
| |
| for (XmlAdapter adapter : getAdapters()) { |
| unmarshaller.setAdapter(adapter); |
| } |
| |
| if (acceptedMediaType == MediaType.APPLICATION_JSON_TYPE) { |
| // Part of the fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=394059 |
| // This issue happens when request has objects derived from an abstract class. |
| // JSON_INCLUDE_ROOT is set to false for JPA-RS. This means JSON requests won't have root tag. |
| // The unmarshal method needs to be called with type, so that moxy can unmarshal the message based on type. |
| // For xml, root tag is always set, unmarshaller must use root of the message for unmarshalling and type should |
| // not be passed to unmarshal for xml type requests. |
| JAXBElement<?> element = unmarshaller.unmarshal(new StreamSource(in), type); |
| if (element.getValue() instanceof List<?>) { |
| for (Object object : (List<?>) element.getValue()) { |
| wrap(object); |
| } |
| return element.getValue(); |
| } else { |
| wrap(element.getValue()); |
| } |
| return element.getValue(); |
| } |
| |
| Object domainObject = unmarshaller.unmarshal(new StreamSource(in)); |
| if (domainObject instanceof List<?>) { |
| for (Object object : (List<?>) domainObject) { |
| wrap(object); |
| } |
| return domainObject; |
| } else { |
| wrap(domainObject); |
| } |
| return domainObject; |
| } |
| |
| /** |
| * Make adjustments to an unmarshalled entity based on what is found in the weaved fields |
| * |
| */ |
| protected Object wrap(Object entity) { |
| if ((entity != null) && (PersistenceWeavedRest.class.isAssignableFrom(entity.getClass()))) { |
| if (!doesExist(null, entity)) { |
| return entity; |
| } |
| ClassDescriptor descriptor = getJAXBDescriptorForClass(entity.getClass()); |
| if (entity instanceof FetchGroupTracker) { |
| FetchGroup fetchGroup = new FetchGroup(); |
| for (DatabaseMapping mapping : descriptor.getMappings()) { |
| if (!(mapping instanceof XMLInverseReferenceMapping)) { |
| fetchGroup.addAttribute(mapping.getAttributeName()); |
| } |
| } |
| (new FetchGroupManager()).setObjectFetchGroup(entity, fetchGroup, null); |
| ((FetchGroupTracker) entity)._persistence_setSession(JpaHelper.getDatabaseSession(getEmf())); |
| } else if (descriptor.hasRelationships()) { |
| for (DatabaseMapping mapping : descriptor.getMappings()) { |
| if (mapping instanceof XMLInverseReferenceMapping) { |
| // we require Fetch groups to handle relationships |
| JPARSLogger.error(getSessionLog(), "weaving_required_for_relationships", new Object[] {}); |
| throw JPARSException.invalidConfiguration(); |
| } |
| } |
| } |
| } |
| return entity; |
| } |
| |
| /** |
| * Marshall an entity to either JSON or XML |
| * Calling this method, will treat relationships as unfetched in the XML/JSON and marshall them as links |
| * rather than attempting to marshall the data in those relationships |
| */ |
| public void marshallEntity(Object object, MediaType mediaType, OutputStream output) throws JAXBException { |
| JPARSLogger.entering(getSessionLog(), CLASS_NAME, "marshallEntity", new Object[] { object, mediaType }); |
| marshall(object, mediaType, output, true); |
| JPARSLogger.exiting(getSessionLog(), CLASS_NAME, "marshallEntity", this, object, mediaType); |
| } |
| |
| /** |
| * Marshall an entity to either JSON or XML. |
| * |
| * @param object the object to marshal. |
| * @param filter the filter (included/excluded fields) to use. |
| * @param mediaType the media type (XML/JSON). |
| * @param output the result. |
| */ |
| public void marshallEntity(Object object, FieldsFilter filter, MediaType mediaType, OutputStream output) throws JAXBException { |
| JPARSLogger.entering(getSessionLog(), CLASS_NAME, "marshallEntity", new Object[] { object, filter, mediaType }); |
| marshall(object, mediaType, output, true, filter); |
| JPARSLogger.exiting(getSessionLog(), CLASS_NAME, "marshallEntity", this, object, mediaType); |
| } |
| |
| /** |
| * Marshall an entity to either JSON or XML. |
| * |
| * @param sendRelationships if this is set to true, relationships will be sent as links instead of sending. |
| * the actual objects in the relationships |
| */ |
| public void marshall(Object object, MediaType mediaType, OutputStream output, boolean sendRelationships) throws JAXBException { |
| marshall(object, mediaType, output, sendRelationships, null); |
| } |
| |
| /** |
| * Marshall an entity to either JSON or XML. |
| * |
| * @param object the object to marshal. |
| * @param mediaType the media type (XML/JSON). |
| * @param output the result. |
| * @param sendRelationships if this is set to true, relationships will be sent as links instead of sending |
| * the actual objects in the relationships. |
| * @param fieldsFilter Specifies fields to include/exclude from the response. |
| */ |
| public void marshall(final Object object, final MediaType mediaType, final OutputStream output, boolean sendRelationships, final FieldsFilter fieldsFilter) throws JAXBException { |
| if (version.compareTo(ServiceVersion.VERSION_2_0) < 0 && sendRelationships) { |
| preMarshallEntity(object); |
| } |
| |
| final Marshaller marshaller = getJAXBContext().createMarshaller(); |
| marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, mediaType.toString()); |
| marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false); |
| marshaller.setProperty(MarshallerProperties.JSON_REDUCE_ANY_ARRAYS, true); |
| marshaller.setProperty(MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); |
| |
| marshaller.setAdapter(new LinkAdapter(getBaseURI().toString(), this)); |
| marshaller.setAdapter(new RelationshipLinkAdapter(getBaseURI().toString(), this)); |
| |
| for (XmlAdapter<?, ?> adapter : getAdapters()) { |
| marshaller.setAdapter(adapter); |
| } |
| |
| // v2.0 and higher |
| if (version.compareTo(ServiceVersion.VERSION_2_0) >= 0) { |
| // Create proxies for collections |
| getCollectionWrapperBuilder().wrapCollections(object); |
| |
| // Build object graph + fields filtering |
| final ObjectGraphBuilder objectGraphBuilder = new ObjectGraphBuilder(this); |
| final ObjectGraph objectGraph = objectGraphBuilder.createObjectGraph(object, fieldsFilter); |
| if (objectGraph != null) { |
| marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, objectGraph); |
| } |
| } |
| |
| if (mediaType == MediaType.APPLICATION_XML_TYPE && object instanceof List) { |
| marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); |
| XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); |
| XMLStreamWriter writer; |
| try { |
| writer = outputFactory.createXMLStreamWriter(output); |
| writer.writeStartDocument(); |
| writer.writeStartElement(ReservedWords.JPARS_LIST_GROUPING_NAME); |
| for (Object o : (List<Object>) object) { |
| marshaller.marshal(o, writer); |
| } |
| writer.writeEndDocument(); |
| writer.flush(); |
| postMarshallEntity(object); |
| } catch (Exception ex) { |
| throw JPARSException.exceptionOccurred(ex); |
| } |
| } else { |
| marshaller.marshal(object, output); |
| if (version.compareTo(ServiceVersion.VERSION_2_0) < 0) { |
| postMarshallEntity(object); |
| } |
| } |
| } |
| |
| /** |
| * Process an entity and add any additional data that needs to be added prior to marshalling |
| * This method will both single entities and lists of entities |
| */ |
| @SuppressWarnings("rawtypes") |
| protected void preMarshallEntity(Object object) { |
| if (object instanceof List) { |
| for (Object o : ((List) object)) { |
| preMarshallIndividualEntity(o); |
| } |
| } else { |
| preMarshallIndividualEntity(object); |
| } |
| } |
| |
| /** |
| * Add any data required prior to marshalling an entity to XML or JSON |
| * In general, this will only affect fields that have been weaved into the object |
| */ |
| @SuppressWarnings("rawtypes") |
| protected void preMarshallIndividualEntity(Object entity) { |
| if (entity instanceof ReportQueryResultListItem) { |
| ReportQueryResultListItem item = (ReportQueryResultListItem) entity; |
| for (JAXBElement field : item.getFields()) { |
| // one or more fields in the MultiResultQueryListItem might be a domain object, |
| // so, we need to set the relationshipInfo for those domain objects. |
| setRelationshipInfo(field.getValue()); |
| } |
| } else if (entity instanceof SingleResultQueryList) { |
| SingleResultQueryList item = (SingleResultQueryList) entity; |
| for (JAXBElement field : item.getFields()) { |
| // one or more fields in the SingleResultQueryList might be a domain object, |
| // so, we need to set the relationshipInfo for those domain objects. |
| setRelationshipInfo(field.getValue()); |
| } |
| } else if (entity instanceof ReportQueryResultList) { |
| ReportQueryResultList list = (ReportQueryResultList) entity; |
| for (ReportQueryResultListItem item : list.getItems()) { |
| for (JAXBElement field : item.getFields()) { |
| // one or more fields in the MultiResultQueryList might be a domain object, |
| // so, we need to set the relationshipInfo for those domain objects. |
| setRelationshipInfo(field.getValue()); |
| } |
| } |
| } else if (entity instanceof ReadAllQueryResultCollection) { |
| ReadAllQueryResultCollection list = (ReadAllQueryResultCollection) entity; |
| List<Object> items = list.getItems(); |
| if ((items != null) && (!items.isEmpty())) { |
| for (Object item : items) { |
| setRelationshipInfo(item); |
| } |
| } |
| } else if (entity instanceof ReportQueryResultCollection) { |
| ReportQueryResultCollection list = (ReportQueryResultCollection) entity; |
| List<ReportQueryResultListItem> items = list.getItems(); |
| if ((items != null) && (!items.isEmpty())) { |
| for (ReportQueryResultListItem item : items) { |
| setRelationshipInfo(item); |
| } |
| } |
| } else { |
| setRelationshipInfo(entity); |
| } |
| } |
| |
| private void setRelationshipInfo(Object entity) { |
| if ((entity != null) && (entity instanceof PersistenceWeavedRest)) { |
| ClassDescriptor descriptor = getServerSession().getClassDescriptor(entity.getClass()); |
| if (descriptor != null) { |
| ((PersistenceWeavedRest) entity)._persistence_setRelationships(new ArrayList<RelationshipInfo>()); |
| for (DatabaseMapping mapping : descriptor.getMappings()) { |
| if (mapping.isForeignReferenceMapping()) { |
| ForeignReferenceMapping frMapping = (ForeignReferenceMapping) mapping; |
| RelationshipInfo info = new RelationshipInfo(); |
| info.setAttributeName(frMapping.getAttributeName()); |
| info.setOwningEntity(entity); |
| info.setOwningEntityAlias(descriptor.getAlias()); |
| info.setPersistencePrimaryKey(descriptor.getObjectBuilder().extractPrimaryKeyFromObject(entity, (AbstractSession) getServerSession())); |
| ((PersistenceWeavedRest) entity)._persistence_getRelationships().add(info); |
| } else if (mapping.isEISMapping()) { |
| if (mapping instanceof EISCompositeCollectionMapping) { |
| EISCompositeCollectionMapping eisMapping = (EISCompositeCollectionMapping) mapping; |
| RelationshipInfo info = new RelationshipInfo(); |
| info.setAttributeName(eisMapping.getAttributeName()); |
| info.setOwningEntity(entity); |
| info.setOwningEntityAlias(descriptor.getAlias()); |
| info.setPersistencePrimaryKey(descriptor.getObjectBuilder().extractPrimaryKeyFromObject(entity, (AbstractSession) getServerSession())); |
| ((PersistenceWeavedRest) entity)._persistence_getRelationships().add(info); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @SuppressWarnings("rawtypes") |
| protected void postMarshallEntity(Object object) { |
| if (object instanceof List) { |
| for (Object entity : ((List) object)) { |
| if (entity instanceof PersistenceWeavedRest) { |
| ((PersistenceWeavedRest) entity)._persistence_setRelationships(new ArrayList<RelationshipInfo>()); |
| } |
| } |
| } else if (object instanceof PersistenceWeavedRest) { |
| ((PersistenceWeavedRest) object)._persistence_setRelationships(new ArrayList<RelationshipInfo>()); |
| } |
| } |
| |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| protected List<XmlAdapter<?, ?>> getAdapters() throws JPARSException { |
| if (adapters != null) { |
| return adapters; |
| } |
| adapters = new ArrayList<XmlAdapter<?, ?>>(); |
| try { |
| final ClassLoader cl = getServerSession().getDatasourcePlatform().getConversionManager().getLoader(); |
| |
| for (ClassDescriptor desc : this.getServerSession().getDescriptors().values()) { |
| if (version.compareTo(ServiceVersion.VERSION_2_0) >= 0) { |
| // Version is 2.0 or higher |
| // Collection adapter |
| final String collectionAdapterName = RestCollectionAdapterClassWriter.getClassName(desc.getJavaClass().getName()); |
| final Class collectionAdaptorClass = Class.forName(collectionAdapterName, true, cl); |
| final Class[] argTypes = {PersistenceContext.class}; |
| final Constructor collectionAdaptorConstructor = collectionAdaptorClass.getDeclaredConstructor(argTypes); |
| final Object[] args = new Object[]{this}; |
| adapters.add((XmlAdapter) collectionAdaptorConstructor.newInstance(args)); |
| |
| // Reference adapter |
| final String refAdapterName = RestReferenceAdapterV2ClassWriter.getClassName(desc.getJavaClass().getName()); |
| final Class refAdaptorClass = Class.forName(refAdapterName, true, cl); |
| final Class[] refAdapterTypes = {PersistenceContext.class}; |
| final Constructor refAdaptorConstructor = refAdaptorClass.getDeclaredConstructor(refAdapterTypes); |
| final Object[] refAdapterArgs = new Object[]{this}; |
| adapters.add((XmlAdapter) refAdaptorConstructor.newInstance(refAdapterArgs)); |
| } else { |
| // Version is 1.0 or below |
| // avoid embeddables |
| if (!desc.isAggregateCollectionDescriptor() && !desc.isAggregateDescriptor()) { |
| Class clz = desc.getJavaClass(); |
| String referenceAdapterName = RestAdapterClassWriter.constructClassNameForReferenceAdapter(clz.getName()); |
| Class referenceAdaptorClass = Class.forName(referenceAdapterName, true, cl); |
| Class[] argTypes1 = {String.class, PersistenceContext.class}; |
| Constructor referenceAdaptorConstructor = referenceAdaptorClass.getDeclaredConstructor(argTypes1); |
| Object[] args1 = new Object[]{getBaseURI().toString(), this}; |
| adapters.add((XmlAdapter) referenceAdaptorConstructor.newInstance(args1)); |
| } |
| } |
| } |
| } catch (RuntimeException | ReflectiveOperationException ex) { |
| ex.printStackTrace(); |
| throw JPARSException.exceptionOccurred(ex); |
| } |
| return adapters; |
| } |
| |
| private boolean getWeavingProperty() { |
| // Initialize the properties with their defaults first |
| boolean restWeavingEnabled = true; |
| boolean fetchGroupWeavingEnabled = true; |
| boolean weavingEnabled = true; |
| |
| Map<String, Object> properties = this.emf.getProperties(); |
| |
| for (Map.Entry<String, Object> entry : properties.entrySet()) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| |
| if (key.equals(PersistenceUnitProperties.WEAVING)) { |
| if (!("true".equalsIgnoreCase((String) value)) && !("static".equalsIgnoreCase((String) value))) { |
| weavingEnabled = false; |
| } |
| } |
| |
| if (key.equals(PersistenceUnitProperties.WEAVING_REST)) { |
| if (!("true".equalsIgnoreCase((String) value))) { |
| restWeavingEnabled = false; |
| } |
| } |
| |
| if (key.equals(PersistenceUnitProperties.WEAVING_FETCHGROUPS)) { |
| if (!("true".equalsIgnoreCase((String) value))) { |
| fetchGroupWeavingEnabled = false; |
| } |
| } |
| } |
| return (weavingEnabled && restWeavingEnabled && fetchGroupWeavingEnabled); |
| } |
| |
| /** |
| * Gets the supported feature set. |
| * |
| * @return the supported feature set. |
| */ |
| public FeatureSet getSupportedFeatureSet() { |
| return version.getFeatureSet(); |
| } |
| |
| /** |
| * Finds out is given query pageable or not. |
| * |
| * @param queryName named query to check. |
| * @return true if pageable, false if not. |
| */ |
| public boolean isQueryPageable(String queryName) { |
| return getPageableQueries().get(queryName) != null; |
| } |
| |
| /** |
| * Gets REST pageable query details by query name. |
| * |
| * @param queryName named query name. |
| * @return RestPageableQuery or null if query couldn't be found. |
| */ |
| public RestPageableQuery getPageableQuery(String queryName) { |
| return getPageableQueries().get(queryName); |
| } |
| |
| /** |
| * Sets the version. |
| * |
| * @param version the new version. |
| */ |
| public void setVersion(String version) { |
| this.version = ServiceVersion.fromCode(version); |
| } |
| |
| /** |
| * Sets the base uri. |
| * |
| * @param baseURI the new base uri |
| */ |
| public void setBaseURI(URI baseURI) { |
| this.baseURI = baseURI; |
| } |
| |
| /** |
| * Getter for pageableQueries property with lazy initialization. |
| * |
| * @return The initialized pageableQueries property. |
| */ |
| private Map<String, RestPageableQuery> getPageableQueries() { |
| // Lazy initialization |
| if (pageableQueries == null) { |
| initPageableQueries(); |
| } |
| |
| return pageableQueries; |
| } |
| |
| /** |
| * Initializes pageableQueries map by reading RestPageableQueries entity annotations. |
| */ |
| private void initPageableQueries() { |
| pageableQueries = new HashMap<String, RestPageableQuery>(); |
| |
| // Iterate on all entity classes |
| for (Class<?> clazz : getServerSession().getProject().getDescriptors().keySet()) { |
| if (clazz.isAnnotationPresent(RestPageableQueries.class)) { |
| final RestPageableQueries restPageableQueries = clazz.getAnnotation(RestPageableQueries.class); |
| |
| // Process each RestPageableQuery annotation in the list |
| for (RestPageableQuery restPageableQuery : restPageableQueries.value()) { |
| pageableQueries.put(restPageableQuery.queryName(), restPageableQuery); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Getter for the collectionWrapperBuilder property with lazy initialization. |
| * |
| * @return the collectionWrapperBuilder. |
| */ |
| public CollectionWrapperBuilder getCollectionWrapperBuilder() { |
| if (collectionWrapperBuilder == null) { |
| collectionWrapperBuilder = new CollectionWrapperBuilder(this); |
| } |
| return collectionWrapperBuilder; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + ((name == null) ? 0 : name.hashCode()); |
| result = prime * result + ((version == null) ? 0 : version.hashCode()); |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; |
| } |
| |
| if (other == null) { |
| return false; |
| } |
| |
| if (getClass() != other.getClass()) { |
| return false; |
| } |
| |
| PersistenceContext otherContext = (PersistenceContext) other; |
| |
| if (name == null) { |
| if (otherContext.name != null) { |
| return false; |
| } |
| } else if (!name.equals(otherContext.name)) { |
| return false; |
| } |
| |
| if (version == null) { |
| if (otherContext.version != null) { |
| return false; |
| } |
| } else if (!version.equals(otherContext.version)) { |
| return false; |
| } |
| |
| return true; |
| } |
| } |