org.eclipse.persistence.internal.jpa.modelgen.objects.PersistenceUnit Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction 180e602
/*******************************************************************************
* Copyright (c) 1998, 2013 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 v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* 08/10/2009-2.0 Guy Pelletier
* - 267391: JPA 2.0 implement/extend/use an APT tooling library for MetaModel API canonical classes
* 11/20/2009-2.0 Guy Pelletier/Mitesh Meswani
* - 295376: Improve usability of MetaModel generator
* 04/27/2010-2.1 Guy Pelletier
* - 309856: MappedSuperclasses from XML are not being initialized properly
* 06/01/2010-2.1 Guy Pelletier
* - 315195: Add new property to avoid reading XML during the canonical model generation
* 11/23/2010-2.2 Guy Pelletier
* - 330660: Canonical model generator throws ClassCastException when using package-info.java
* 02/14/2013-2.5 Guy Pelletier
* - 338610: JPA 2.1 Functionality for Java EE 7 (JSR-338)
******************************************************************************/
package org.eclipse.persistence.internal.jpa.modelgen.objects;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic.Kind;
import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitInfo;
import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitProperty;
import org.eclipse.persistence.internal.jpa.metadata.MetadataHelper;
import org.eclipse.persistence.internal.jpa.metadata.MetadataProject;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EmbeddableAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.MappedSuperclassAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappingsReader;
import org.eclipse.persistence.internal.jpa.modelgen.MetadataMirrorFactory;
import org.eclipse.persistence.internal.jpa.modelgen.objects.PersistenceUnitReader;
import org.eclipse.persistence.oxm.XMLContext;
import static org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProperties.CANONICAL_MODEL_SUB_PACKAGE;
import static org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProperties.CANONICAL_MODEL_PREFIX;
import static org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProperties.CANONICAL_MODEL_SUFFIX;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_EMBEDDABLE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ENTITY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_MAPPED_SUPERCLASS;
/**
* A representation of a persistence unit definition.
*
* @author Guy Pelletier, Doug Clarke
* @since EclipseLink 1.2
*/
public class PersistenceUnit {
protected List xmlEntityMappings;
protected MetadataProject project;
protected MetadataMirrorFactory factory;
protected PersistenceUnitReader persistenceUnitReader;
protected ProcessingEnvironment processingEnv;
protected SEPersistenceUnitInfo persistenceUnitInfo;
protected HashMap persistenceUnitProperties;
/**
* INTERNAL:
*/
public PersistenceUnit(SEPersistenceUnitInfo puInfo, MetadataMirrorFactory mirrorFactory, PersistenceUnitReader reader) {
factory = mirrorFactory;
persistenceUnitInfo = puInfo;
persistenceUnitReader = reader;
// Ask the factory for the project and processing environment
processingEnv = factory.getProcessingEnvironment();
project = factory.getMetadataProject(persistenceUnitInfo);
// Init our OX mapped properties into a map.
initPersistenceUnitProperties();
// Init our mapping files. This method relies on properties being
// initialized first.
initXMLEntityMappings();
}
/**
* INTERNAL:
* Add an element decorated with an Embeddable annotation.
*
* The possibilities here:
* 1 - New element and not exclude unlisted classes - add it
* 2 - New element but exclude unlisted classes - ignore it.
* 3 - Existing element, but accessor loaded from XML - don't touch it
* 4 - Existing element, but accessor loaded from Annotations - add new accessor overriding the old.
*/
public void addEmbeddableAccessor(Element element) {
MetadataClass metadataClass = factory.getMetadataClass(element);
// Remove the accessor from other maps if the type changed.
removeEntityAccessor(metadataClass);
removeMappedSuperclassAccessor(metadataClass);
if (project.hasEmbeddable(metadataClass)) {
EmbeddableAccessor embeddableAccessor = project.getEmbeddableAccessor(metadataClass);
// If it was loaded from XML, reset the pre-processed flag.
if (embeddableAccessor.loadedFromXML()) {
embeddableAccessor.clearPreProcessed();
} else {
// Was not loaded from XML and existed in the project.
if (excludeUnlistedClasses(metadataClass)) {
// Exclude unlisted classes is now false, remove it!
removeEmbeddableAccessor(metadataClass);
} else {
// Otherwise, override the existing accessor!
addEmbeddableAccessor(new EmbeddableAccessor(metadataClass.getAnnotation(JPA_EMBEDDABLE), metadataClass, project));
}
}
} else if (! excludeUnlistedClasses(metadataClass)) {
// add it!
addEmbeddableAccessor(new EmbeddableAccessor(metadataClass.getAnnotation(JPA_EMBEDDABLE), metadataClass, project));
}
}
/**
* INTERNAL:
* Add an embeddable accessor to the project, preserving any previous
* owning descriptors set if applicable.
*/
protected void addEmbeddableAccessor(EmbeddableAccessor embeddableAccessor) {
// We need to preserve owning descriptors to ensure we process
// an embeddable accessor in the correct context. That is, if the
// user changed only the embeddable class then we won't process
// the owning entity (which ensures the owning descriptor, itself,
// is set on the embeddable accessor).
// If ownership changed, then those entities involved will be
// in the round elements and we will correctly set the owning
// descriptor in the pre-process stage of those entities, overriding
// this setting)
if (project.hasEmbeddable(embeddableAccessor.getJavaClass())) {
EmbeddableAccessor existingEmbeddableAccessor = project.getEmbeddableAccessor(embeddableAccessor.getJavaClass());
embeddableAccessor.addEmbeddingAccessors(existingEmbeddableAccessor.getEmbeddingAccessors());
embeddableAccessor.addOwningDescriptors(existingEmbeddableAccessor.getOwningDescriptors());
}
project.addEmbeddableAccessor(embeddableAccessor);
}
/**
* INTERNAL:
* Add an element decorated with an Entity annotation.
*
* The possibilities here:
* 1 - New element and not exclude unlisted classes - add it
* 2 - New element but exclude unlisted classes - ignore it.
* 3 - Existing element, loaded from XML - don't touch it
* 4 - Existing element, loaded from Annotations - add new accessor overriding the old.
*/
public void addEntityAccessor(Element element) {
MetadataClass metadataClass = factory.getMetadataClass(element);
// Remove the accessor from other maps if the type changed.
removeEmbeddableAccessor(metadataClass);
removeMappedSuperclassAccessor(metadataClass);
if (project.hasEntity(metadataClass)) {
EntityAccessor entityAccessor = project.getEntityAccessor(metadataClass);
// If it was loaded from XML, reset the pre-processed flag.
if (entityAccessor.loadedFromXML()) {
entityAccessor.clearPreProcessed();
} else {
// Was not loaded from XML and existed in the project.
if (excludeUnlistedClasses(metadataClass)) {
// Exclude unlisted classes is now false, remove it!
removeEntityAccessor(metadataClass);
} else {
// Otherwise, override the existing accessor!
project.addEntityAccessor(new EntityAccessor(metadataClass.getAnnotation(JPA_ENTITY), metadataClass, project));
}
}
} else if (! excludeUnlistedClasses(metadataClass)) {
// add it!
project.addEntityAccessor(new EntityAccessor(metadataClass.getAnnotation(JPA_ENTITY), metadataClass, project));
}
}
/**
* INTERNAL:
* Add an element decorated with a MappedSuperclass annotation.
*
* The possibilities here:
* 1 - New element and not exclude unlisted classes - add it
* 2 - New element but exclude unlisted classes - ignore it.
* 3 - Existing element, but accessor loaded from XML - don't touch it
* 4 - Existing element, but accessor loaded from Annotations - add new accessor overridding the old.
*/
public void addMappedSuperclassAccessor(Element element) {
MetadataClass metadataClass = factory.getMetadataClass(element);
// Remove the accessor from other maps if the type changed.
removeEntityAccessor(metadataClass);
removeEmbeddableAccessor(metadataClass);
if (project.hasMappedSuperclass(metadataClass)) {
MappedSuperclassAccessor mappedSuperclassAccessor = project.getMappedSuperclassAccessor(metadataClass);
// If it was loaded from XML, reset the pre-processed flag.
if (mappedSuperclassAccessor.loadedFromXML()) {
mappedSuperclassAccessor.clearPreProcessed();
} else {
// Was not loaded from XML and existed in the project.
if (excludeUnlistedClasses(metadataClass)) {
// Exclude unlisted classes is now false, remove it!
project.removeMappedSuperclassAccessor(metadataClass);
} else {
// Otherwise, override the existing accessor!
project.addMappedSuperclass(new MappedSuperclassAccessor(metadataClass.getAnnotation(JPA_MAPPED_SUPERCLASS), metadataClass, project));
}
}
} else if (! excludeUnlistedClasses(metadataClass)) {
// add it!
project.addMappedSuperclass(new MappedSuperclassAccessor(metadataClass.getAnnotation(JPA_MAPPED_SUPERCLASS), metadataClass, project));
}
}
/**
* INTERNAL:
*/
protected void addPropertyFromOptions(String propertyName) {
if (! persistenceUnitProperties.containsKey(propertyName) || persistenceUnitProperties.get(propertyName) == null) {
persistenceUnitProperties.put(propertyName, processingEnv.getOptions().get(propertyName));
}
}
/**
* INTERNAL:
*/
protected void addXMLEntityMappings(String mappingFile, XMLContext context) {
// If the input stream is null, we didn't find the mapping file, so
// don't bother trying to read it.
InputStream inputStream = persistenceUnitReader.getInputStream(mappingFile, false);
if (inputStream != null) {
try {
XMLEntityMappings entityMappings = (XMLEntityMappings) context.createUnmarshaller().unmarshal(inputStream);
// For eclipselink-orm merging and overriding these need to be set.
entityMappings.setIsEclipseLinkORMFile(mappingFile.equals(MetadataHelper.ECLIPSELINK_ORM_FILE));
entityMappings.setMappingFile(mappingFile);
processingEnv.getMessager().printMessage(Kind.NOTE, "File loaded : " + mappingFile + ", is eclipselink-orm file: " + entityMappings.isEclipseLinkORMFile());
xmlEntityMappings.add(entityMappings);
} finally {
persistenceUnitReader.closeInputStream(inputStream);
}
}
}
/**
* INTERNAL:
*/
protected void addXMLEntityMappings(String mappingFile) {
try {
// Try eclipselink project
addXMLEntityMappings(mappingFile, XMLEntityMappingsReader.getEclipseLinkOrmProject());
} catch (XMLMarshalException e) {
try {
// Try JPA 2.1 project
addXMLEntityMappings(mappingFile, XMLEntityMappingsReader.getOrm2_1Project());
} catch (XMLMarshalException ee) {
try {
// Try JPA 2.0 project
addXMLEntityMappings(mappingFile, XMLEntityMappingsReader.getOrm2_0Project());
} catch (XMLMarshalException eee) {
// Try JPA 1.0 project (don't catch exceptions at this point)
addXMLEntityMappings(mappingFile, XMLEntityMappingsReader.getOrm1_0Project());
}
}
}
}
/**
* INTERNAL:
* If the accessor is no longer valid it we should probably look into
* removing the accessor from the project. For now I don't think it's
* a big deal.
*/
public boolean containsClass(MetadataClass metadataClass) {
if (project.hasEntity(metadataClass)) {
return isValidAccessor(project.getEntityAccessor(metadataClass), metadataClass.getAnnotation(JPA_ENTITY));
}
if (project.hasEmbeddable(metadataClass)) {
return isValidAccessor(project.getEmbeddableAccessor(metadataClass), metadataClass.getAnnotation(JPA_EMBEDDABLE));
}
if (project.hasMappedSuperclass(metadataClass)) {
return isValidAccessor(project.getMappedSuperclassAccessor(metadataClass), metadataClass.getAnnotation(JPA_MAPPED_SUPERCLASS));
}
return false;
}
/**
* INTERNAL:
* Return true if the metadata class given is not a managed class and
* exclude-unlisted-classes is set to true for this PU.
*/
protected boolean excludeUnlistedClasses(MetadataClass cls) {
return (! persistenceUnitInfo.getManagedClassNames().contains(cls.getName())) && persistenceUnitInfo.excludeUnlistedClasses();
}
/**
* INTERNAL:
*/
public ClassAccessor getClassAccessor(MetadataClass metadataClass) {
if (project.hasEntity(metadataClass)) {
return project.getEntityAccessor(metadataClass);
}
if (project.hasEmbeddable(metadataClass)) {
return project.getEmbeddableAccessor(metadataClass);
}
if (project.hasMappedSuperclass(metadataClass)) {
return project.getMappedSuperclassAccessor(metadataClass);
}
return null;
}
/**
* INTERNAL:
*/
public String getQualifiedCanonicalName(String qualifiedName) {
return MetadataHelper.getQualifiedCanonicalName(qualifiedName, persistenceUnitProperties);
}
/**
* INTERNAL:
*/
public void initPersistenceUnitProperties() {
persistenceUnitProperties = new HashMap();
// Determine how much validation we want to do. For now, last one
// in wins (in the case of multiple settings) and we ignore null
// named properties.
for (SEPersistenceUnitProperty property : persistenceUnitInfo.getPersistenceUnitProperties()) {
if (property.getName() != null) {
//processingEnv.getMessager().printMessage(Kind.NOTE, "Key: " + property.getName() + " , value: " + property.getValue());
persistenceUnitProperties.put(property.getName(), property.getValue());
}
}
// Check for user specified options and add them to the properties
// if they were not specified in the persistence.xml.
addPropertyFromOptions(CANONICAL_MODEL_PREFIX);
addPropertyFromOptions(CANONICAL_MODEL_SUFFIX);
addPropertyFromOptions(CANONICAL_MODEL_SUB_PACKAGE);
}
/**
* INTERNAL:
*/
protected void initXMLEntityMappings() {
xmlEntityMappings = new ArrayList();
// Load the orm.xml if it exists.
addXMLEntityMappings(MetadataHelper.JPA_ORM_FILE);
// Load the eclipselink-orm.xml if it exists and is not excluded.
Boolean excludeEclipseLinkORM = false;
if (persistenceUnitProperties.containsKey(PersistenceUnitProperties.EXCLUDE_ECLIPSELINK_ORM_FILE)) {
excludeEclipseLinkORM = Boolean.valueOf((String) persistenceUnitProperties.get(PersistenceUnitProperties.EXCLUDE_ECLIPSELINK_ORM_FILE));
}
if (! excludeEclipseLinkORM) {
addXMLEntityMappings(MetadataHelper.ECLIPSELINK_ORM_FILE);
}
// Load the listed mapping files.
for (String mappingFile : persistenceUnitInfo.getMappingFileNames()) {
if (! mappingFile.equals(MetadataHelper.JPA_ORM_FILE) && ! mappingFile.equals(MetadataHelper.ECLIPSELINK_ORM_FILE)) {
addXMLEntityMappings(mappingFile);
}
}
// 1 - Iterate through the classes that are defined in the
// files and add them to the map. This will merge the accessors where
// necessary.
for (XMLEntityMappings entityMappings : xmlEntityMappings) {
entityMappings.setLoader(factory.getLoader());
entityMappings.setProject(project);
entityMappings.setMetadataFactory(factory);
entityMappings.setLoadedForCanonicalModel(true);
// Process the persistence unit metadata if defined.
entityMappings.processPersistenceUnitMetadata();
}
// 2 - Iterate through the classes that are defined in the
// files and add them to the map. This will merge the accessors where
// necessary.
HashMap entities = new HashMap();
HashMap embeddables = new HashMap();
for (XMLEntityMappings entityMappings : xmlEntityMappings) {
entityMappings.initPersistenceUnitClasses(entities, embeddables);
}
// 3 - Iterate through all the XML entities and add them to the project
// and apply any persistence unit defaults.
for (EntityAccessor entity : entities.values()) {
// This will apply global persistence unit defaults.
project.addEntityAccessor(entity);
// This will override any global settings.
entity.getEntityMappings().processEntityMappingsDefaults(entity);
}
// 4 - Iterate though all the XML embeddables and add them to the
// project and apply any persistence unit defaults.
for (EmbeddableAccessor embeddable : embeddables.values()) {
// This will apply global persistence unit defaults.
addEmbeddableAccessor(embeddable);
// This will override any global settings.
embeddable.getEntityMappings().processEntityMappingsDefaults(embeddable);
}
}
/**
* INTERNAL:
*/
protected boolean isValidAccessor(ClassAccessor accessor, MetadataAnnotation annotation) {
if (! accessor.loadedFromXML()) {
// If it wasn't loaded from XML, we need to look at it further. It
// could have been deleted and brought back (without an annotation)
// or simply have had its annotation removed. Check for an annotation.
return annotation != null;
}
return true;
}
/**
* INTERNAL:
* Steps are important here, so don't change them.
*/
public void preProcessForCanonicalModel() {
// 1 - Pre-Process the list of entities first. This will discover/build
// the list of embeddable accessors.
for (EntityAccessor entityAccessor : project.getEntityAccessors()) {
// Some entity accessors can be fast tracked for pre-processing.
// That is, an inheritance subclass will tell its parents to
// pre-process. So don't pre-process it again.
if (shouldPreProcess(entityAccessor)) {
// Update the accessible object in case it changed.
entityAccessor.setAccessibleObject(factory.getMetadataClass(entityAccessor.getAccessibleObjectName()));
// Pre-process the accessor now.
entityAccessor.preProcessForCanonicalModel();
}
}
// 2 - Pre-Process the list of mapped superclasses.
for (MappedSuperclassAccessor mappedSuperclassAccessor : project.getMappedSuperclasses()) {
if (shouldPreProcess(mappedSuperclassAccessor)) {
// Update the accessible object first, in case it changed.
mappedSuperclassAccessor.setAccessibleObject(factory.getMetadataClass(mappedSuperclassAccessor.getAccessibleObjectName()));
// Pre-process the accessor now.
mappedSuperclassAccessor.preProcessForCanonicalModel();
}
}
// 3 - Pre-process the list of root embeddable accessors. This list will
// have been built from step 1. Root embeddable accessors will have
// an owning descriptor (used to determine access type).
for (EmbeddableAccessor embeddableAccessor : project.getRootEmbeddableAccessors()) {
if (shouldPreProcess(embeddableAccessor)) {
// Update the accessible object first, in case it changed.
embeddableAccessor.setAccessibleObject(factory.getMetadataClass(embeddableAccessor.getAccessibleObjectName()));
// Pre-process the accessor now.
embeddableAccessor.preProcessForCanonicalModel();
}
}
// 4 - Go through the whole list of embeddables and process any that
// were not pre-processed. Only way this is true is if the embeddable
// was not referenced from an entity (be it root or nested in a root
// embeddable)
for (EmbeddableAccessor embeddableAccessor : project.getEmbeddableAccessors()) {
if (shouldPreProcess(embeddableAccessor)) {
// Update the accessible object first, in case it changed.
embeddableAccessor.setAccessibleObject(factory.getMetadataClass(embeddableAccessor.getAccessibleObjectName()));
// Pre-process the accessor now.
embeddableAccessor.preProcessForCanonicalModel();
}
}
}
/**
* INTERNAL:
* If it is not already pre-processed or is included in the round elements
* (presumably because something changed), then pre-process the accessor.
* You may get a previously loaded and processed XML class accessor that
* may have had a change made to its associated class (that does not have
* either an Entity, Embeddable or MappedSuperclass annotation) with the
* load xml flag turned off. Therefore we must make sure we preProcess the
* accessor again.
*/
protected boolean shouldPreProcess(ClassAccessor accessor) {
MetadataClass cls = (MetadataClass) accessor.getAccessibleObject();
if (accessor.isPreProcessed() && factory.isRoundElement(cls)) {
accessor.clearPreProcessed();
}
return ! accessor.isPreProcessed();
}
/**
* INTERNAL:
* Remove the entity accessor for the given metadata class if one exists.
*/
protected void removeEntityAccessor(MetadataClass metadataClass) {
if (project.hasEntity(metadataClass)) {
project.removeEntityAccessor(metadataClass);
}
}
/**
* INTERNAL:
* Remove the embeddable accessor for the given metadata class if one exists.
*/
protected void removeEmbeddableAccessor(MetadataClass metadataClass) {
if (project.hasEmbeddable(metadataClass)) {
project.removeEmbeddableAccessor(metadataClass);
}
}
/**
* INTERNAL:
* Remove the embeddable accessor for the given metadata class if one exists.
*/
protected void removeMappedSuperclassAccessor(MetadataClass metadataClass) {
if (project.hasMappedSuperclass(metadataClass)) {
project.removeMappedSuperclassAccessor(metadataClass);
}
}
/**
* INTERNAL:
*/
@Override
public String toString() {
return persistenceUnitInfo.getPersistenceUnitName();
}
}