com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager Maven / Gradle / Ivy
/**
* Copyright (C) 2006 Robin Bygrave
*
* This file is part of Ebean.
*
* Ebean is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Ebean is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Ebean; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.avaje.ebeaninternal.server.deploy;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.config.EncryptKey;
import com.avaje.ebean.config.EncryptKeyManager;
import com.avaje.ebean.config.GlobalProperties;
import com.avaje.ebean.config.NamingConvention;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.DbIdentity;
import com.avaje.ebean.config.dbplatform.IdGenerator;
import com.avaje.ebean.config.dbplatform.IdType;
import com.avaje.ebean.config.lucene.IndexDefn;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebean.validation.factory.LengthValidatorFactory;
import com.avaje.ebean.validation.factory.NotNullValidatorFactory;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.TransactionEventTable;
import com.avaje.ebeaninternal.server.core.BootupClasses;
import com.avaje.ebeaninternal.server.core.ConcurrencyMode;
import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.core.InternalConfiguration;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.XmlConfig;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderEmbedded;
import com.avaje.ebeaninternal.server.deploy.id.IdBinderFactory;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanTable;
import com.avaje.ebeaninternal.server.deploy.meta.DeployTableJoin;
import com.avaje.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import com.avaje.ebeaninternal.server.deploy.parse.DeployCreateProperties;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInherit;
import com.avaje.ebeaninternal.server.deploy.parse.DeployUtil;
import com.avaje.ebeaninternal.server.deploy.parse.ReadAnnotations;
import com.avaje.ebeaninternal.server.deploy.parse.TransientProperties;
import com.avaje.ebeaninternal.server.idgen.UuidIdGenerator;
import com.avaje.ebeaninternal.server.lib.util.Dnode;
import com.avaje.ebeaninternal.server.lucene.LIndex;
import com.avaje.ebeaninternal.server.lucene.LuceneIndexManager;
import com.avaje.ebeaninternal.server.reflect.BeanReflect;
import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory;
import com.avaje.ebeaninternal.server.subclass.SubClassManager;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
import com.avaje.ebeaninternal.server.type.TypeManager;
/**
* Creates BeanDescriptors.
*/
public class BeanDescriptorManager implements BeanDescriptorMap {
private static final Logger logger = Logger.getLogger(BeanDescriptorManager.class.getName());
private static final BeanDescComparator beanDescComparator = new BeanDescComparator();
private final ReadAnnotations readAnnotations = new ReadAnnotations();
private final TransientProperties transientProperties;
/**
* Helper to derive inheritance information.
*/
private final DeployInherit deplyInherit;
private final BeanReflectFactory reflectFactory;
private final DeployUtil deployUtil;
private final TypeManager typeManager;
private final PersistControllerManager persistControllerManager;
private final BeanFinderManager beanFinderManager;
private final PersistListenerManager persistListenerManager;
private final BeanQueryAdapterManager beanQueryAdapterManager;
private final SubClassManager subClassManager;
private final NamingConvention namingConvention;
private final DeployCreateProperties createProperties;
private final DeployOrmXml deployOrmXml;
private final BeanManagerFactory beanManagerFactory;
private int enhancedClassCount;
private int subclassClassCount;
private final HashSet subclassedEntities = new HashSet();
private final boolean updateChangesOnly;
private final BootupClasses bootupClasses;
private final String serverName;
private Map, DeployBeanInfo>> deplyInfoMap = new HashMap, DeployBeanInfo>>();
private final Map, BeanTable> beanTableMap = new HashMap, BeanTable>();
private final Map> descMap = new HashMap>();
private final Map> idDescMap = new HashMap>();
private final Map> beanManagerMap = new HashMap>();
private final Map>> tableToDescMap = new HashMap>>();
private List> immutableDescriptorList;
private final Set descriptorUniqueIds = new HashSet();
private final DbIdentity dbIdentity;
private final DataSource dataSource;
private final DatabasePlatform databasePlatform;
private final UuidIdGenerator uuidIdGenerator = new UuidIdGenerator();
private final ServerCacheManager cacheManager;
private final BackgroundExecutor backgroundExecutor;
private final int dbSequenceBatchSize;
private final EncryptKeyManager encryptKeyManager;
private final IdBinderFactory idBinderFactory;
private final XmlConfig xmlConfig;
private final LuceneIndexManager luceneManager;
/**
* Create for a given database dbConfig.
*/
public BeanDescriptorManager(InternalConfiguration config, LuceneIndexManager luceneManager) {
this.luceneManager = luceneManager;
this.serverName = InternString.intern(config.getServerConfig().getName());
this.cacheManager = config.getCacheManager();
this.xmlConfig = config.getXmlConfig();
this.dbSequenceBatchSize = config.getServerConfig().getDatabaseSequenceBatchSize();
this.backgroundExecutor = config.getBackgroundExecutor();
this.dataSource = config.getServerConfig().getDataSource();
this.encryptKeyManager = config.getServerConfig().getEncryptKeyManager();
this.databasePlatform = config.getServerConfig().getDatabasePlatform();
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm());
this.bootupClasses = config.getBootupClasses();
this.createProperties = config.getDeployCreateProperties();
this.subClassManager = config.getSubClassManager();
this.typeManager = config.getTypeManager();
this.namingConvention = config.getServerConfig().getNamingConvention();
this.dbIdentity = config.getDatabasePlatform().getDbIdentity();
this.deplyInherit = config.getDeployInherit();
this.deployOrmXml = config.getDeployOrmXml();
this.deployUtil = config.getDeployUtil();
this.beanManagerFactory = new BeanManagerFactory(config.getServerConfig(), config.getDatabasePlatform());
this.updateChangesOnly = config.getServerConfig().isUpdateChangesOnly();
this.persistControllerManager = new PersistControllerManager(bootupClasses);
this.persistListenerManager = new PersistListenerManager(bootupClasses);
this.beanQueryAdapterManager = new BeanQueryAdapterManager(bootupClasses);
this.beanFinderManager = new DefaultBeanFinderManager();
this.reflectFactory = createReflectionFactory();
this.transientProperties = new TransientProperties();
}
public BeanDescriptor> getBeanDescriptorById(String descriptorId) {
return idDescMap.get(descriptorId);
}
@SuppressWarnings("unchecked")
public BeanDescriptor getBeanDescriptor(Class entityType) {
// remove $$EntityBean stuff
String className = SubClassUtil.getSuperClassName(entityType.getName());
return (BeanDescriptor) descMap.get(className);
}
@SuppressWarnings("unchecked")
public BeanDescriptor getBeanDescriptor(String entityClassName) {
// remove $$EntityBean stuff
entityClassName = SubClassUtil.getSuperClassName(entityClassName);
return (BeanDescriptor) descMap.get(entityClassName);
}
public String getServerName() {
return serverName;
}
public ServerCacheManager getCacheManager() {
return cacheManager;
}
public NamingConvention getNamingConvention() {
return namingConvention;
}
/**
* Set the internal EbeanServer instance to all BeanDescriptors.
*/
public void setEbeanServer(SpiEbeanServer internalEbean) {
for (BeanDescriptor> desc : immutableDescriptorList) {
desc.setEbeanServer(internalEbean);
}
}
public IdBinder createIdBinder(BeanProperty[] uids) {
return idBinderFactory.createIdBinder(uids);
}
public void deploy() {
try {
createListeners();
readEmbeddedDeployment();
readEntityDeploymentInitial();
assignLuceneIndexDefns();
readEntityBeanTable();
readEntityDeploymentAssociations();
readInheritedIdGenerators();
// creates the BeanDescriptors
readEntityRelationships();
readRawSqlQueries();
List> list = new ArrayList>(descMap.values());
Collections.sort(list, beanDescComparator);
immutableDescriptorList = Collections.unmodifiableList(list);
// put into map using the "desriptorId" (alternative to class name)
for (BeanDescriptor> d : list) {
idDescMap.put(d.getDescriptorId(), d);
}
initialiseAll();
readForeignKeys();
readTableToDescriptor();
logStatus();
deplyInfoMap.clear();
deplyInfoMap = null;
} catch (RuntimeException e) {
String msg = "Error in deployment";
logger.log(Level.SEVERE, msg, e);
throw e;
}
}
/**
* Return the Encrypt key given the table and column name.
*/
public EncryptKey getEncryptKey(String tableName, String columnName) {
return encryptKeyManager.getEncryptKey(tableName, columnName);
}
/**
* For SQL based modifications we need to invalidate appropriate parts of
* the cache.
*/
public void cacheNotify(TransactionEventTable.TableIUD tableIUD) {
List> list = getBeanDescriptors(tableIUD.getTable());
if (list != null) {
for (int i = 0; i < list.size(); i++) {
list.get(i).cacheNotify(tableIUD);
}
}
}
/**
* Return the BeanDescriptors mapped to the table.
*/
public List> getBeanDescriptors(String tableName) {
return tableToDescMap.get(tableName.toLowerCase());
}
/**
* Build a map of table names to BeanDescriptors.
*
* This is generally used to maintain caches from table names.
*
*/
private void readTableToDescriptor() {
for (BeanDescriptor> desc : descMap.values()) {
String baseTable = desc.getBaseTable();
if (baseTable == null) {
} else {
baseTable = baseTable.toLowerCase();
List> list = tableToDescMap.get(baseTable);
if (list == null) {
list = new ArrayList>(1);
tableToDescMap.put(baseTable, list);
}
list.add(desc);
}
}
}
private void readForeignKeys() {
for (BeanDescriptor> d : descMap.values()) {
d.initialiseFkeys();
}
}
/**
* Initialise all the BeanDescriptors.
*
* This occurs after all the BeanDescriptors have been created. This
* resolves circular relationships between BeanDescriptors.
*
*
* Also responsible for creating all the BeanManagers which contain the
* persister, listener etc.
*
*/
private void initialiseAll() {
// now that all the BeanDescriptors are in their map
// we can initialise them which sorts out circular
// dependencies for OneToMany and ManyToOne etc
// PASS 1:
// initialise the ID properties of all the beans
// first (as they are needed to initialise the
// associated properties in the second pass).
for (BeanDescriptor> d : descMap.values()) {
d.initialiseId();
}
// PASS 2:
// now initialise all the inherit info
for (BeanDescriptor> d : descMap.values()) {
d.initInheritInfo();
}
// PASS 3:
// now initialise all the associated properties
for (BeanDescriptor> d : descMap.values()) {
d.initialiseOther();
}
for (BeanDescriptor> d : descMap.values()) {
IndexDefn> luceneIndexDefn = d.getLuceneIndexDefn();
if (luceneIndexDefn != null){
try {
LIndex luceneIndex = luceneManager.create(luceneIndexDefn, d);
d.setLuceneIndex(luceneIndex);
} catch (IOException e) {
String msg = "Error creating Lucene Index "+luceneIndexDefn.getClass().getName();
logger.log(Level.SEVERE, msg, e);
}
}
}
// create BeanManager for each non-embedded entity bean
for (BeanDescriptor> d : descMap.values()) {
if (!d.isEmbedded()) {
BeanManager> m = beanManagerFactory.create(d);
beanManagerMap.put(d.getFullName(), m);
checkForValidEmbeddedId(d);
}
}
}
private void checkForValidEmbeddedId(BeanDescriptor> d) {
IdBinder idBinder = d.getIdBinder();
if (idBinder != null && idBinder instanceof IdBinderEmbedded) {
IdBinderEmbedded embId = (IdBinderEmbedded) idBinder;
BeanDescriptor> idBeanDescriptor = embId.getIdBeanDescriptor();
Class> idType = idBeanDescriptor.getBeanType();
try {
idType.getDeclaredMethod("hashCode", new Class[] {});
idType.getDeclaredMethod("equals", new Class[] { Object.class });
} catch (NoSuchMethodException e) {
checkMissingHashCodeOrEquals(e, idType, d.getBeanType());
}
}
}
private void checkMissingHashCodeOrEquals(Exception source, Class> idType, Class> beanType) {
String msg = "SERIOUS ERROR: The hashCode() and equals() methods *MUST* be implemented ";
msg += "on Embedded bean " + idType + " as it is used as an Id for " + beanType;
if (GlobalProperties.getBoolean("ebean.strict", true)) {
throw new PersistenceException(msg, source);
} else {
logger.log(Level.SEVERE, msg, source);
}
}
/**
* Return an immutable list of all the BeanDescriptors.
*/
public List> getBeanDescriptorList() {
return immutableDescriptorList;
}
public Map, BeanTable> getBeanTables() {
return beanTableMap;
}
public BeanTable getBeanTable(Class> type) {
return beanTableMap.get(type);
}
public Map> getBeanDescriptors() {
return descMap;
}
@SuppressWarnings("unchecked")
public BeanManager getBeanManager(Class entityType) {
return (BeanManager) getBeanManager(entityType.getName());
}
public BeanManager> getBeanManager(String beanClassName) {
beanClassName = SubClassUtil.getSuperClassName(beanClassName);
return beanManagerMap.get(beanClassName);
}
public DNativeQuery getNativeQuery(String name) {
return deployOrmXml.getNativeQuery(name);
}
private void assignLuceneIndexDefns() {
List> list = bootupClasses.getLuceneIndexInstances();
for (int i = 0; i < list.size(); i++) {
IndexDefn> indexDefn = list.get(i);
Class> entityClass = getIndexDefnEntityClass(indexDefn.getClass());
DeployBeanInfo> deployBeanInfo = deplyInfoMap.get(entityClass);
if (deployBeanInfo == null){
String msg = "Could not find entity deployment for "+entityClass;
throw new PersistenceException(msg);
}
deployBeanInfo.getDescriptor().setIndexDefn(indexDefn);
}
}
private Class> getIndexDefnEntityClass(Class> controller){
Class> cls = ParamTypeUtil.findParamType(controller, IndexDefn.class);
if (cls == null){
String msg = "Could not determine the entity class (generics parameter type) from "+controller+" using reflection.";
throw new PersistenceException(msg);
}
return cls;
}
/**
* Create the BeanControllers, BeanFinders and BeanListeners.
*/
private void createListeners() {
int qa = beanQueryAdapterManager.getRegisterCount();
int cc = persistControllerManager.getRegisterCount();
int lc = persistListenerManager.getRegisterCount();
int fc = beanFinderManager.createBeanFinders(bootupClasses.getBeanFinders());
logger.fine("BeanPersistControllers[" + cc + "] BeanFinders[" + fc + "] BeanPersistListeners[" + lc
+ "] BeanQueryAdapters[" + qa + "]");
}
/**
* Log Warning if mixing subclass and enhancement.
*
* If enhancement is used for some classes it is expected to be used for all
* and vice versa.
*
*/
private void logStatus() {
String msg = "Entities enhanced[" + enhancedClassCount + "] subclassed[" + subclassClassCount + "]";
logger.info(msg);
if (enhancedClassCount > 0) {
if (subclassClassCount > 0) {
String subclassEntityNames = subclassedEntities.toString();
String m = "Mixing enhanced and subclassed entities. Subclassed classes:" + subclassEntityNames;
logger.warning(m);
}
}
}
private BeanDescriptor createEmbedded(Class beanClass) {
DeployBeanInfo info = createDeployBeanInfo(beanClass);
readDeployAssociations(info);
Integer key = getUniqueHash(info.getDescriptor());
return new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString());
}
private void registerBeanDescriptor(BeanDescriptor> desc) {
descMap.put(desc.getBeanType().getName(), desc);
}
/**
* Read deployment information for all the embedded beans.
*/
private void readEmbeddedDeployment() {
ArrayList> embeddedClasses = bootupClasses.getEmbeddables();
for (int i = 0; i < embeddedClasses.size(); i++) {
Class> cls = embeddedClasses.get(i);
if (logger.isLoggable(Level.FINER)) {
String msg = "load deployinfo for embeddable:" + cls.getName();
logger.finer(msg);
}
BeanDescriptor> embDesc = createEmbedded(cls);
registerBeanDescriptor(embDesc);
}
}
/**
* Read the initial deployment information for the entities.
*
* This stops short of reading relationship meta data until after the
* BeanTables have all been created.
*
*/
private void readEntityDeploymentInitial() {
ArrayList> entityClasses = bootupClasses.getEntities();
for (Class> entityClass : entityClasses) {
DeployBeanInfo> info = createDeployBeanInfo(entityClass);
deplyInfoMap.put(entityClass, info);
}
}
/**
* Create the BeanTable information which has the base table and id.
*
* This is determined prior to resolving relationship information.
*
*/
private void readEntityBeanTable() {
Iterator> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo> info = it.next();
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
}
}
/**
* Create the BeanTable information which has the base table and id.
*
* This is determined prior to resolving relationship information.
*
*/
private void readEntityDeploymentAssociations() {
Iterator> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo> info = it.next();
readDeployAssociations(info);
}
}
private void readInheritedIdGenerators() {
Iterator> it = deplyInfoMap.values().iterator();
while (it.hasNext()) {
DeployBeanInfo> info = it.next();
DeployBeanDescriptor> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
DeployBeanInfo> rootBeanInfo = deplyInfoMap.get(inheritInfo.getRoot().getType());
IdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator();
if (rootIdGen != null) {
descriptor.setIdGenerator(rootIdGen);
}
}
}
}
/**
* Create the BeanTable from the deployment information gathered so far.
*/
private BeanTable createBeanTable(DeployBeanInfo> info) {
DeployBeanDescriptor> deployDescriptor = info.getDescriptor();
DeployBeanTable beanTable = deployDescriptor.createDeployBeanTable();
return new BeanTable(beanTable, this);
}
/**
* Parse the named Raw Sql queries using BeanDescriptor.
*/
private void readRawSqlQueries() {
for (DeployBeanInfo> info : deplyInfoMap.values()) {
DeployBeanDescriptor> deployDesc = info.getDescriptor();
BeanDescriptor> desc = getBeanDescriptor(deployDesc.getBeanType());
for (DRawSqlMeta rawSqlMeta : deployDesc.getRawSqlMeta()) {
if (rawSqlMeta.getQuery() == null) {
} else {
DeployNamedQuery nq = new DRawSqlSelectBuilder(namingConvention, desc, rawSqlMeta).parse();
desc.addNamedQuery(nq);
}
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void readEntityRelationships() {
// We only perform 'circular' checks etc after we have
// all the DeployBeanDescriptors created and in the map.
for (DeployBeanInfo> info : deplyInfoMap.values()) {
checkMappedBy(info);
}
for (DeployBeanInfo> info : deplyInfoMap.values()) {
secondaryPropsJoins(info);
}
for (DeployBeanInfo> info : deplyInfoMap.values()) {
DeployBeanDescriptor> deployBeanDescriptor = info.getDescriptor();
Integer key = getUniqueHash(deployBeanDescriptor);
registerBeanDescriptor(new BeanDescriptor(this, typeManager, info.getDescriptor(), key.toString()));
}
}
private Integer getUniqueHash(DeployBeanDescriptor> deployBeanDescriptor) {
int hashCode = deployBeanDescriptor.getFullName().hashCode();
for (int i = 0; i < 100000; i++) {
Integer key = Integer.valueOf(hashCode+i);
if (!descriptorUniqueIds.contains(key)){
return key;
}
}
throw new RuntimeException("Failed to generate a unique hash for "+deployBeanDescriptor.getFullName());
}
private void secondaryPropsJoins(DeployBeanInfo> info) {
DeployBeanDescriptor> descriptor = info.getDescriptor();
for (DeployBeanProperty prop : descriptor.propertiesBase()) {
if (prop.isSecondaryTable()) {
String tableName = prop.getSecondaryTable();
// find a join to that table...
DeployBeanPropertyAssocOne> assocOne = descriptor.findJoinToTable(tableName);
if (assocOne == null){
String msg = "Error with property "+prop.getFullBeanName()
+ ". Could not find a Relationship to table "+tableName
+ ". Perhaps you could use a @JoinColumn instead.";
throw new RuntimeException(msg);
}
DeployTableJoin tableJoin = assocOne.getTableJoin();
prop.setSecondaryTableJoin(tableJoin, assocOne.getName());
}
}
}
/**
* Check the mappedBy attributes for properties on this descriptor.
*
* This will read join information defined on the 'owning/other' side of the
* relationship. It also does some extra work for unidirectional
* relationships.
*
*/
private void checkMappedBy(DeployBeanInfo> info) {
for (DeployBeanPropertyAssocOne> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
if (oneProp.getMappedBy() != null) {
checkMappedByOneToOne(info, oneProp);
}
}
}
for (DeployBeanPropertyAssocMany> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
if (manyProp.isManyToMany()) {
checkMappedByManyToMany(info, manyProp);
} else {
checkMappedByOneToMany(info, manyProp);
}
}
}
}
private DeployBeanDescriptor> getTargetDescriptor(DeployBeanPropertyAssoc> prop) {
Class> targetType = prop.getTargetType();
DeployBeanInfo> info = deplyInfoMap.get(targetType);
if (info == null) {
String msg = "Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName();
throw new PersistenceException(msg);
}
return info.getDescriptor();
}
/**
* Check that the many property has either an implied mappedBy property or
* mark it as unidirectional.
*/
private boolean findMappedBy(DeployBeanPropertyAssocMany> prop) {
// this is the entity bean type - that owns this property
Class> owningType = prop.getOwningType();
Set matchSet = new HashSet();
// get the bean descriptor that holds the mappedBy property
DeployBeanDescriptor> targetDesc = getTargetDescriptor(prop);
List> ones = targetDesc.propertiesAssocOne();
for (DeployBeanPropertyAssocOne> possibleMappedBy : ones) {
Class> possibleMappedByType = possibleMappedBy.getTargetType();
if (possibleMappedByType.equals(owningType)) {
prop.setMappedBy(possibleMappedBy.getName());
matchSet.add(possibleMappedBy.getName());
}
}
if (matchSet.size() == 0) {
// this is a unidirectional relationship
// ... that is no matching property on the 'detail' bean
return false;
}
if (matchSet.size() == 1) {
// all right with the world
return true;
}
if (matchSet.size() == 2) {
// try to find a match implicitly using a common naming convention
// e.g. List loggedBugs; ... search for "logged" in matchSet
String name = prop.getName();
// get the target type short name
String targetType = prop.getTargetType().getName();
String shortTypeName = targetType.substring(targetType.lastIndexOf(".") + 1);
// name includes (probably ends with) the target type short name?
int p = name.indexOf(shortTypeName);
if (p > 1) {
// ok, get the 'interesting' part of the property name
// That is the name without the target type
String searchName = name.substring(0, p).toLowerCase();
// search for this in the possible matches
Iterator it = matchSet.iterator();
while (it.hasNext()) {
String possibleMappedBy = it.next();
String possibleLower = possibleMappedBy.toLowerCase();
if (possibleLower.indexOf(searchName) > -1) {
// we have a match..
prop.setMappedBy(possibleMappedBy);
String m = "Implicitly found mappedBy for " + targetDesc + "." + prop;
m += " by searching for [" + searchName + "] against " + matchSet;
logger.fine(m);
return true;
}
}
}
}
// multiple options so should specify mappedBy property
String msg = "Error on " + prop.getFullBeanName() + " missing mappedBy.";
msg += " There are [" + matchSet.size() + "] possible properties in " + targetDesc;
msg += " that this association could be mapped to. Please specify one using ";
msg += "the mappedBy attribute on @OneToMany.";
throw new PersistenceException(msg);
}
/**
* A OneToMany with no matching mappedBy property in the target so must be
* unidirectional.
*
* This means that inserts MUST cascade for this property.
*
*
* Create a "Shadow"/Unidirectional property on the target. It is used with
* inserts to set the foreign key value (e.g. inserts the foreign key value
* into the order_id column on the order_lines table).
*
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private void makeUnidirectional(DeployBeanInfo> info, DeployBeanPropertyAssocMany> oneToMany) {
DeployBeanDescriptor> targetDesc = getTargetDescriptor(oneToMany);
Class> owningType = oneToMany.getOwningType();
if (!oneToMany.getCascadeInfo().isSave()) {
// The property MUST have persist cascading so that inserts work.
Class> targetType = oneToMany.getTargetType();
String msg = "Error on " + oneToMany.getFullBeanName() + ". @OneToMany MUST have ";
msg += "Cascade.PERSIST or Cascade.ALL because this is a unidirectional ";
msg += "relationship. That is, there is no property of type " + owningType + " on " + targetType;
throw new PersistenceException(msg);
}
// mark this property as unidirectional
oneToMany.setUnidirectional(true);
// create the 'shadow' unidirectional property
// which is put on the target descriptor
DeployBeanPropertyAssocOne> unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType);
unidirectional.setUndirectionalShadow(true);
unidirectional.setNullable(false);
unidirectional.setDbRead(true);
unidirectional.setDbInsertable(true);
unidirectional.setDbUpdateable(false);
targetDesc.setUnidirectional(unidirectional);
// specify table and table alias...
BeanTable beanTable = getBeanTable(owningType);
unidirectional.setBeanTable(beanTable);
unidirectional.setName(beanTable.getBaseTable());
info.setBeanJoinType(unidirectional, true);
// define the TableJoin
DeployTableJoin oneToManyJoin = oneToMany.getTableJoin();
if (!oneToManyJoin.hasJoinColumns()) {
throw new RuntimeException("No join columns");
}
// inverse of the oneToManyJoin
DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin();
unidirectionalJoin.setColumns(oneToManyJoin.columns(), true);
}
private void checkMappedByOneToOne(DeployBeanInfo> info, DeployBeanPropertyAssocOne> prop) {
// check that the mappedBy property is valid and read
// its associated join information if it is available
String mappedBy = prop.getMappedBy();
// get the mappedBy property
DeployBeanDescriptor> targetDesc = getTargetDescriptor(prop);
DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy);
if (mappedProp == null) {
String m = "Error on " + prop.getFullBeanName();
m += " Can not find mappedBy property [" + targetDesc + "." + mappedBy + "] ";
throw new PersistenceException(m);
}
if (!(mappedProp instanceof DeployBeanPropertyAssocOne>)) {
String m = "Error on " + prop.getFullBeanName();
m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?";
throw new PersistenceException(m);
}
DeployBeanPropertyAssocOne> mappedAssocOne = (DeployBeanPropertyAssocOne>) mappedProp;
if (!mappedAssocOne.isOneToOne()) {
String m = "Error on " + prop.getFullBeanName();
m += ". mappedBy property [" + targetDesc + "." + mappedBy + "]is not a OneToOne?";
throw new PersistenceException(m);
}
DeployTableJoin tableJoin = prop.getTableJoin();
if (!tableJoin.hasJoinColumns()) {
// define Join as the inverse of the mappedBy property
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable());
}
}
/**
* If the property has mappedBy set then do two things. Make sure the
* mappedBy property exists, and secondly read its join information.
*
* We can use the join information from the mappedBy property and reverse it
* for using in the OneToMany direction.
*
*/
private void checkMappedByOneToMany(DeployBeanInfo> info, DeployBeanPropertyAssocMany> prop) {
// get the bean descriptor that holds the mappedBy property
if (prop.getMappedBy() == null) {
if (!findMappedBy(prop)) {
makeUnidirectional(info, prop);
return;
}
}
// check that the mappedBy property is valid and read
// its associated join information if it is available
String mappedBy = prop.getMappedBy();
// get the mappedBy property
DeployBeanDescriptor> targetDesc = getTargetDescriptor(prop);
DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy);
if (mappedProp == null) {
String m = "Error on " + prop.getFullBeanName();
m += " Can not find mappedBy property [" + mappedBy + "] ";
m += "in [" + targetDesc + "]";
throw new PersistenceException(m);
}
if (!(mappedProp instanceof DeployBeanPropertyAssocOne>)) {
String m = "Error on " + prop.getFullBeanName();
m += ". mappedBy property [" + mappedBy + "]is not a ManyToOne?";
m += "in [" + targetDesc + "]";
throw new PersistenceException(m);
}
DeployBeanPropertyAssocOne> mappedAssocOne = (DeployBeanPropertyAssocOne>) mappedProp;
DeployTableJoin tableJoin = prop.getTableJoin();
if (!tableJoin.hasJoinColumns()) {
// define Join as the inverse of the mappedBy property
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
otherTableJoin.copyTo(tableJoin, true, tableJoin.getTable());
}
}
/**
* For mappedBy copy the joins from the other side.
*/
private void checkMappedByManyToMany(DeployBeanInfo> info, DeployBeanPropertyAssocMany> prop) {
// get the bean descriptor that holds the mappedBy property
String mappedBy = prop.getMappedBy();
if (mappedBy == null) {
return;
}
// get the mappedBy property
DeployBeanDescriptor> targetDesc = getTargetDescriptor(prop);
DeployBeanProperty mappedProp = targetDesc.getBeanProperty(mappedBy);
if (mappedProp == null) {
String m = "Error on " + prop.getFullBeanName();
m += " Can not find mappedBy property [" + mappedBy + "] ";
m += "in [" + targetDesc + "]";
throw new PersistenceException(m);
}
if (!(mappedProp instanceof DeployBeanPropertyAssocMany>)) {
String m = "Error on " + prop.getFullBeanName();
m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?";
throw new PersistenceException(m);
}
DeployBeanPropertyAssocMany> mappedAssocMany = (DeployBeanPropertyAssocMany>) mappedProp;
if (!mappedAssocMany.isManyToMany()) {
String m = "Error on " + prop.getFullBeanName();
m += ". mappedBy property [" + targetDesc + "." + mappedBy + "] is not a ManyToMany?";
throw new PersistenceException(m);
}
// define the relationships/joins on this side as the
// reverse of the other mappedBy side ...
// DeployTableJoin mappedJoin = mappedAssocMany.getTableJoin();
DeployTableJoin mappedIntJoin = mappedAssocMany.getIntersectionJoin();
DeployTableJoin mappendInverseJoin = mappedAssocMany.getInverseJoin();
String intTableName = mappedIntJoin.getTable();
DeployTableJoin tableJoin = prop.getTableJoin();
mappedIntJoin.copyTo(tableJoin, true, targetDesc.getBaseTable());
DeployTableJoin intJoin = new DeployTableJoin();
mappendInverseJoin.copyTo(intJoin, false, intTableName);
prop.setIntersectionJoin(intJoin);
DeployTableJoin inverseJoin = new DeployTableJoin();
mappedIntJoin.copyTo(inverseJoin, false, intTableName);
prop.setInverseJoin(inverseJoin);
}
private void setBeanControllerFinderListener(DeployBeanDescriptor descriptor) {
Class beanType = descriptor.getBeanType();
persistControllerManager.addPersistControllers(descriptor);
persistListenerManager.addPersistListeners(descriptor);
beanQueryAdapterManager.addQueryAdapter(descriptor);
BeanFinder beanFinder = beanFinderManager.getBeanFinder(beanType);
if (beanFinder != null) {
descriptor.setBeanFinder(beanFinder);
logger.fine("BeanFinder on[" + descriptor.getFullName() + "] " + beanFinder.getClass().getName());
}
}
/**
* Read the initial deployment information for a given bean type.
*/
private DeployBeanInfo createDeployBeanInfo(Class beanClass) {
DeployBeanDescriptor desc = new DeployBeanDescriptor(beanClass);
desc.setUpdateChangesOnly(updateChangesOnly);
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
createProperties.createProperties(desc);
DeployBeanInfo info = new DeployBeanInfo(deployUtil, desc);
readAnnotations.readInitial(info);
return info;
}
private void readDeployAssociations(DeployBeanInfo info) {
DeployBeanDescriptor desc = info.getDescriptor();
readAnnotations.readAssociations(info, this);
readXml(desc);
if (!EntityType.ORM.equals(desc.getEntityType())){
// not using base table
desc.setBaseTable(null);
}
// mark transient properties
transientProperties.process(desc);
setScalarType(desc);
if (!desc.isEmbedded()) {
// Set IdGenerator or use DB Identity
setIdGeneration(desc);
// find the appropriate default concurrency mode
setConcurrencyMode(desc);
}
autoAddValidators(desc);
// generate the byte code
createByteCode(desc);
}
/**
* Set the Identity generation mechanism.
*/
private IdType setIdGeneration(DeployBeanDescriptor desc) {
if (desc.propertiesId().size() == 0) {
// bean doen't have an Id property
if (!desc.isBaseTableType() || desc.getBeanFinder() != null) {
// using BeanFinder so perhaps valid without an id
} else {
// expecting an id property
logger.warning(Message.msg("deploy.nouid", desc.getFullName()));
}
return null;
}
if (IdType.SEQUENCE.equals(desc.getIdType()) && !dbIdentity.isSupportsSequence()) {
// explicit sequence but not supported by the DatabasePlatform
logger.info("Explicit sequence on " + desc.getFullName() + " but not supported by DB Platform - ignored");
desc.setIdType(null);
}
if (IdType.IDENTITY.equals(desc.getIdType()) && !dbIdentity.isSupportsIdentity()) {
// explicit identity but not supported by the DatabasePlatform
logger.info("Explicit Identity on " + desc.getFullName() + " but not supported by DB Platform - ignored");
desc.setIdType(null);
}
if (desc.getIdType() == null) {
// use the default. IDENTITY or SEQUENCE.
desc.setIdType(dbIdentity.getIdType());
}
if (IdType.GENERATOR.equals(desc.getIdType())) {
String genName = desc.getIdGeneratorName();
if (UuidIdGenerator.AUTO_UUID.equals(genName)) {
desc.setIdGenerator(uuidIdGenerator);
return IdType.GENERATOR;
}
}
if (desc.getBaseTable() == null) {
// no base table so not going to set Identity
// of sequence information
return null;
}
if (IdType.IDENTITY.equals(desc.getIdType())) {
// used when getGeneratedKeys is not supported (SQL Server 2000)
String selectLastInsertedId = dbIdentity.getSelectLastInsertedId(desc.getBaseTable());
desc.setSelectLastInsertedId(selectLastInsertedId);
return IdType.IDENTITY;
}
String seqName = desc.getIdGeneratorName();
if (seqName != null) {
logger.fine("explicit sequence " + seqName + " on " + desc.getFullName());
} else {
String primaryKeyColumn = desc.getSinglePrimaryKeyColumn();
// use namingConvention to define sequence name
seqName = namingConvention.getSequenceName(desc.getBaseTable(), primaryKeyColumn);
}
// create the sequence based IdGenerator
IdGenerator seqIdGen = createSequenceIdGenerator(seqName);
desc.setIdGenerator(seqIdGen);
return IdType.SEQUENCE;
}
private IdGenerator createSequenceIdGenerator(String seqName) {
return databasePlatform.createSequenceIdGenerator(backgroundExecutor, dataSource, seqName, dbSequenceBatchSize);
}
private void createByteCode(DeployBeanDescriptor> deploy) {
// check to see if the bean supports EntityBean interface
// generate a subclass if required
setEntityBeanClass(deploy);
// use Code generation or Standard reflection to support
// getter and setter methods
setBeanReflect(deploy);
}
/**
* Add Length and NotNull validators based on Column annotation etc.
*/
private void autoAddValidators(DeployBeanDescriptor> deployDesc) {
for (DeployBeanProperty prop : deployDesc.propertiesBase()) {
autoAddValidators(prop);
}
}
/**
* Add Length and NotNull validators based on Column annotation etc.
*/
private void autoAddValidators(DeployBeanProperty prop) {
if (String.class.equals(prop.getPropertyType()) && prop.getDbLength() > 0) {
// check if the property already has the LengthValidator
if (!prop.containsValidatorType(LengthValidatorFactory.LengthValidator.class)) {
prop.addValidator(LengthValidatorFactory.create(0, prop.getDbLength()));
}
}
if (!prop.isNullable() && !prop.isId() && !prop.isGenerated()) {
// check if the property already has the NotNullValidator
if (!prop.containsValidatorType(NotNullValidatorFactory.NotNullValidator.class)) {
prop.addValidator(NotNullValidatorFactory.NOT_NULL);
}
}
}
/**
* Set the Scalar Types on all the simple types. This is done AFTER
* transients have been identified. This is because a non-transient field
* MUST have a ScalarType. It is useful for transients to have ScalarTypes
* because then they can be used in a SqlSelect query.
*
* Enums are treated a bit differently in that they always have a ScalarType
* as one is built for them.
*
*/
private void setScalarType(DeployBeanDescriptor> deployDesc) {
Iterator it = deployDesc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
if (prop instanceof DeployBeanPropertyAssoc>) {
} else {
deployUtil.setScalarType(prop);
}
}
}
private void readXml(DeployBeanDescriptor> deployDesc) {
List eXml = xmlConfig.findEntityXml(deployDesc.getFullName());
readXmlRawSql(deployDesc, eXml);
Dnode entityXml = deployOrmXml.findEntityDeploymentXml(deployDesc.getFullName());
if (entityXml != null) {
readXmlNamedQueries(deployDesc, entityXml);
readXmlSql(deployDesc, entityXml);
}
}
/**
* Read sql-select (FUTURE: additionally sql-insert, sql-update,
* sql-delete). If found this entity bean is based on raw sql.
*/
private void readXmlSql(DeployBeanDescriptor> deployDesc, Dnode entityXml) {
List sqlSelectList = entityXml.findAll("sql-select", entityXml.getLevel() + 1);
for (int i = 0; i < sqlSelectList.size(); i++) {
Dnode sqlSelect = sqlSelectList.get(i);
readSqlSelect(deployDesc, sqlSelect);
}
}
private String findContent(Dnode node, String nodeName) {
Dnode found = node.find(nodeName);
if (found != null) {
return found.getNodeContent();
} else {
return null;
}
}
private void readSqlSelect(DeployBeanDescriptor> deployDesc, Dnode sqlSelect) {
String name = sqlSelect.getStringAttr("name", "default");
String extend = sqlSelect.getStringAttr("extend", null);
String queryDebug = sqlSelect.getStringAttr("debug", null);
boolean debug = (queryDebug != null && queryDebug.equalsIgnoreCase("true"));
// the raw sql select
String query = findContent(sqlSelect, "query");
String where = findContent(sqlSelect, "where");
String having = findContent(sqlSelect, "having");
String columnMapping = findContent(sqlSelect, "columnMapping");
DRawSqlMeta m = new DRawSqlMeta(name, extend, query, debug, where, having, columnMapping);
deployDesc.add(m);
}
private void readXmlRawSql(DeployBeanDescriptor> deployDesc, List entityXml) {
List rawSqlQueries = xmlConfig.find(entityXml, "raw-sql");
for (int i = 0; i < rawSqlQueries.size(); i++) {
Dnode rawSqlDnode = rawSqlQueries.get(i);
String name = rawSqlDnode.getAttribute("name");
if (isEmpty(name)){
throw new IllegalStateException("raw-sql for "+deployDesc.getFullName()+" missing name attribute");
}
Dnode queryNode = rawSqlDnode.find("query");
if (queryNode == null){
throw new IllegalStateException("raw-sql for "+deployDesc.getFullName()+" missing query element");
}
String sql = queryNode.getNodeContent();
if (isEmpty(sql)){
throw new IllegalStateException("raw-sql for "+deployDesc.getFullName()+" has empty sql in the query element?");
}
List columnMappings = rawSqlDnode.findAll("columnMapping", 1);
RawSqlBuilder rawSqlBuilder = RawSqlBuilder.parse(sql);
for (int j = 0; j < columnMappings.size(); j++) {
Dnode cm = columnMappings.get(j);
String column = cm.getAttribute("column");
String property = cm.getAttribute("property");
rawSqlBuilder.columnMapping(column, property);
}
RawSql rawSql = rawSqlBuilder.create();
DeployNamedQuery namedQuery = new DeployNamedQuery(name, rawSql);
deployDesc.add(namedQuery);
}
}
private boolean isEmpty(String s){
return s == null || s.trim().length() == 0;
}
/**
* Read named queries for this bean type.
*/
private void readXmlNamedQueries(DeployBeanDescriptor> deployDesc, Dnode entityXml) {
// look for named-query...
List namedQueries = entityXml.findAll("named-query", 1);
for (Dnode namedQueryXml : namedQueries) {
String name = (String) namedQueryXml.getAttribute("name");
Dnode query = namedQueryXml.find("query");
if (query == null) {
logger.warning("orm.xml " + deployDesc.getFullName() + " named-query missing query element?");
} else {
String oql = query.getNodeContent();
// TODO: QueryHints not read from xml yet
if (name == null || oql == null) {
logger.warning("orm.xml " + deployDesc.getFullName() + " named-query has no query content?");
} else {
// add the named query
DeployNamedQuery q = new DeployNamedQuery(name, oql, null);
deployDesc.add(q);
}
}
}
}
private BeanReflectFactory createReflectionFactory() {
return new EnhanceBeanReflectFactory();
}
/**
* Set BeanReflect BeanReflectGetter and BeanReflectSetter properties.
*
* This sets the implementation of constructing entity beans and the setting
* and getting of properties. It is generally faster to use code generation
* rather than reflection to do this.
*
*/
private void setBeanReflect(DeployBeanDescriptor> desc) {
// Set the BeanReflectGetter and BeanReflectSetter that typically
// use generated code. NB: Due to Bug 166 so now doing this for
// abstract classes as well.
Class> beanType = desc.getBeanType();
Class> factType = desc.getFactoryType();
BeanReflect beanReflect = reflectFactory.create(beanType, factType);
desc.setBeanReflect(beanReflect);
try {
Iterator it = desc.propertiesAll();
while (it.hasNext()) {
DeployBeanProperty prop = it.next();
String propName = prop.getName();
if (desc.isAbstract() || beanReflect.isVanillaOnly()) {
// use reflection in the case of imported abstract class
// with
// inheritance. Refer Bug 166
prop.setGetter(ReflectGetter.create(prop));
prop.setSetter(ReflectSetter.create(prop));
} else {
// use generated code for getting setting property values
BeanReflectGetter getter = beanReflect.getGetter(propName);
BeanReflectSetter setter = beanReflect.getSetter(propName);
prop.setGetter(getter);
prop.setSetter(setter);
if (getter == null) {
// should never happen
String m = "BeanReflectGetter for " + prop.getFullBeanName() + " was not found?";
throw new RuntimeException(m);
}
}
}
} catch (IllegalArgumentException e) {
Class> superClass = desc.getBeanType().getSuperclass();
String msg = "Error with [" + desc.getFullName() + "] I believe it is not enhanced but it's superClass ["
+ superClass + "] is?"
+ " (You are not allowed to mix enhancement in a single inheritance hierarchy)";
throw new PersistenceException(msg, e);
}
}
/**
* DevNote: It is assumed that Embedded can contain version properties. It
* is also assumed that Embedded beans do NOT themselves contain Embedded
* beans which contain version properties.
*/
private void setConcurrencyMode(DeployBeanDescriptor> desc) {
if (!desc.getConcurrencyMode().equals(ConcurrencyMode.ALL)) {
// concurrency mode explicitly set during deployment
return;
}
if (checkForVersionProperties(desc)) {
desc.setConcurrencyMode(ConcurrencyMode.VERSION);
}
}
/**
* Search for version properties also including embedded beans.
*/
private boolean checkForVersionProperties(DeployBeanDescriptor> desc) {
boolean hasVersionProperty = false;
List props = desc.propertiesBase();
for (int i = 0; i < props.size(); i++) {
if (props.get(i).isVersionColumn()) {
hasVersionProperty = true;
}
}
return hasVersionProperty;
}
private boolean hasEntityBeanInterface(Class> beanClass) {
Class>[] interfaces = beanClass.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(EntityBean.class)) {
return true;
}
}
return false;
}
/**
* Test the bean type to see if it implements EntityBean interface already.
*/
private void setEntityBeanClass(DeployBeanDescriptor> desc) {
Class> beanClass = desc.getBeanType();
if (desc.isAbstract()) {
if (hasEntityBeanInterface(beanClass)) {
checkEnhanced(desc, beanClass);
} else {
checkSubclass(desc, beanClass);
}
return;
}
try {
Object testBean = null;
try {
testBean = beanClass.newInstance();
} catch (InstantiationException e){
// expected when no default constructor
logger.fine("no default constructor on "+beanClass+" e:"+e);
} catch (IllegalAccessException e){
// expected when no default constructor
logger.fine("no default constructor on "+beanClass+" e:"+e);
}
if (testBean instanceof EntityBean == false) {
checkSubclass(desc, beanClass);
} else {
String className = beanClass.getName();
try {
// check that it really is enhanced (rather than mixed
// enhancement)
String marker = ((EntityBean) testBean)._ebean_getMarker();
if (!marker.equals(className)) {
String msg = "Error with [" + desc.getFullName()
+ "] It has not been enhanced but it's superClass [" + beanClass.getSuperclass()
+ "] is?"
+ " (You are not allowed to mix enhancement in a single inheritance hierarchy)"
+ " marker[" + marker + "] className[" + className + "]";
throw new PersistenceException(msg);
}
} catch (AbstractMethodError e) {
throw new PersistenceException(
"Old Ebean v1.0 enhancement detected in Ebean v1.1 - please do a clean enhancement.", e);
}
checkEnhanced(desc, beanClass);
}
} catch (PersistenceException ex) {
throw ex;
} catch (Exception ex) {
throw new PersistenceException(ex);
}
}
private void checkEnhanced(DeployBeanDescriptor> desc, Class> beanClass) {
// the bean already implements EntityBean
checkInheritedClasses(true, beanClass);
desc.setFactoryType(beanClass);
if (!beanClass.getName().startsWith("com.avaje.ebean.meta")) {
enhancedClassCount++;
}
}
private void checkSubclass(DeployBeanDescriptor> desc, Class> beanClass) {
checkInheritedClasses(false, beanClass);
desc.checkReadAndWriteMethods();
subclassClassCount++;
EntityType entityType = desc.getEntityType();
if (EntityType.XMLELEMENT.equals(entityType)){
desc.setFactoryType(beanClass);
} else {
Class> subClass = subClassManager.resolve(beanClass.getName());
desc.setFactoryType(subClass);
subclassedEntities.add(desc.getName());
}
}
/**
* Check that the inherited classes are the same as the entity bean (aka all
* enhanced or all dynamically subclassed).
*/
private void checkInheritedClasses(boolean ensureEnhanced, Class> beanClass) {
Class> superclass = beanClass.getSuperclass();
if (Object.class.equals(superclass)) {
// we got to the top of the inheritance
return;
}
boolean isClassEnhanced = EntityBean.class.isAssignableFrom(superclass);
if (ensureEnhanced != isClassEnhanced) {
String msg;
if (ensureEnhanced) {
msg = "Class [" + superclass + "] is not enhanced and [" + beanClass + "] is - (you can not mix!!)";
} else {
msg = "Class [" + superclass + "] is enhanced and [" + beanClass + "] is not - (you can not mix!!)";
}
throw new IllegalStateException(msg);
}
// recursively continue up the inheritance hierarchy
checkInheritedClasses(ensureEnhanced, superclass);
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
private static final class BeanDescComparator implements Comparator>, Serializable {
private static final long serialVersionUID = 1L;
public int compare(BeanDescriptor> o1, BeanDescriptor> o2) {
return o1.getName().compareTo(o2.getName());
}
}
}