use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.
the class CmpJpaConversion method processEntityBean.
/**
* Generate the CMP mapping data for an individual
* EntityBean.
*
* @param ejbModule The module containing the bean.
* @param entityMappings The accumulated set of entity mappings.
* @param bean The been we're generating the mapping for.
*/
private void processEntityBean(final EjbModule ejbModule, final EntityMappings entityMappings, final EntityBean bean) {
// try to add a new persistence-context-ref for cmp
if (!addPersistenceContextRef(bean)) {
// which means it has a mapping, so skip this bean
return;
}
// get the real bean class
final Class ejbClass = loadClass(ejbModule.getClassLoader(), bean.getEjbClass());
// and generate a name for the subclass that will be generated and handed to the JPA
// engine as the managed class.
final String jpaEntityClassName = CmpUtil.getCmpImplClassName(bean.getAbstractSchemaName(), ejbClass.getName());
// We don't use this mapping directly, instead we pull entries from it
// the reason being is that we intend to support mappings that aren't
// exactly correct. i.e. users should be able to write mappings completely
// ignorant of the fact that we subclass. The fact that we subclass means
// these user supplied mappings might need to be adjusted as the jpa orm.xml
// file is extremely subclass/supperclass aware and mappings specified in it
// need to be spot on.
final EntityMappings userMappings = getUserEntityMappings(ejbModule);
// chain of the bean looking for any that have user defined mappings.
for (Class clazz = ejbClass; clazz != null; clazz = clazz.getSuperclass()) {
final MappedSuperclass mappedSuperclass = removeMappedSuperclass(userMappings, clazz.getName());
// that the mapping is correct. Copy it from their mappings to ours
if (mappedSuperclass != null) {
entityMappings.getMappedSuperclass().add(mappedSuperclass);
}
}
// Look for an existing mapping using the openejb generated subclass name
Entity entity = removeEntity(userMappings, jpaEntityClassName);
// because we are going to ignore all other xml metadata.
if (entity != null) {
// XmlMetadataComplete is an OpenEJB specific flag that
// tells all other legacy descriptor converters to keep
// their hands off.
entity.setXmlMetadataComplete(true);
entityMappings.getEntity().add(entity);
return;
}
if (entity == null) {
entity = new Entity(jpaEntityClassName);
}
// have to check for null everywhere.
if (entity.getAttributes() == null) {
entity.setAttributes(new Attributes());
}
// add the entity
entityMappings.getEntity().add(entity);
// OVERWRITE: description: contains the name of the entity bean
entity.setDescription(ejbModule.getModuleId() + "#" + bean.getEjbName());
// PRESERVE has queries: name: the name of the entity in queries
final String entityName = bean.getAbstractSchemaName();
entity.setName(entityName);
entity.setEjbName(bean.getEjbName());
final ClassLoader classLoader = ejbModule.getClassLoader();
final Collection<MappedSuperclass> mappedSuperclasses;
if (bean.getCmpVersion() == CmpVersion.CMP2) {
// perform the 2.x class mapping. This really just identifies the primary key and
// other cmp fields that will be generated for the concrete class and identify them
// to JPA.
mappedSuperclasses = mapClass2x(entity, bean, classLoader);
} else {
// map the cmp class, but if we are using a mapped super class,
// generate attribute-override instead of id and basic
mappedSuperclasses = mapClass1x(bean.getEjbClass(), entity, bean, classLoader);
}
// configuration. f
if (mappedSuperclasses != null) {
// that will get passed to the JPA engine.
for (final MappedSuperclass mappedSuperclass : mappedSuperclasses) {
entityMappings.getMappedSuperclass().add(mappedSuperclass);
}
}
// process queries
for (final Query query : bean.getQuery()) {
final NamedQuery namedQuery = new NamedQuery();
final QueryMethod queryMethod = query.getQueryMethod();
// todo deployment id could change in one of the later conversions... use entity name instead, but we need to save it off
final StringBuilder name = new StringBuilder();
name.append(entityName).append(".").append(queryMethod.getMethodName());
if (queryMethod.getMethodParams() != null && !queryMethod.getMethodParams().getMethodParam().isEmpty()) {
name.append('(');
boolean first = true;
for (final String methodParam : queryMethod.getMethodParams().getMethodParam()) {
if (!first) {
name.append(",");
}
name.append(methodParam);
first = false;
}
name.append(')');
}
namedQuery.setName(name.toString());
namedQuery.setQuery(query.getEjbQl());
entity.getNamedQuery().add(namedQuery);
}
// todo: there should be a common interface between ejb query object and openejb query object
final OpenejbJar openejbJar = ejbModule.getOpenejbJar();
final EjbDeployment ejbDeployment = openejbJar.getDeploymentsByEjbName().get(bean.getEjbName());
if (ejbDeployment != null) {
for (final org.apache.openejb.jee.oejb3.Query query : ejbDeployment.getQuery()) {
final NamedQuery namedQuery = new NamedQuery();
final org.apache.openejb.jee.oejb3.QueryMethod queryMethod = query.getQueryMethod();
// todo deployment id could change in one of the later conversions... use entity name instead, but we need to save it off
final StringBuilder name = new StringBuilder();
name.append(entityName).append(".").append(queryMethod.getMethodName());
if (queryMethod.getMethodParams() != null && !queryMethod.getMethodParams().getMethodParam().isEmpty()) {
name.append('(');
boolean first = true;
for (final String methodParam : queryMethod.getMethodParams().getMethodParam()) {
if (!first) {
name.append(",");
}
name.append(methodParam);
first = false;
}
name.append(')');
}
namedQuery.setName(name.toString());
namedQuery.setQuery(query.getObjectQl());
entity.getNamedQuery().add(namedQuery);
}
}
}
use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.
the class CmpJpaConversion method mapClass2x.
/**
* Generate the JPA mapping for a CMP 2.x bean. Since
* the field accessors are all defined as abstract methods
* and the fields will not be defined in the implementation
* class, we don't need to deal with mapped superclasses.
* All of the fields and concrete methods will be
* implemented by the generated subclass, so from
* a JPA standpoint, there are no mapped superclasses
* required.
*
* @param mapping The mapping information we're updating.
* @param bean The entity bean meta data
* @param classLoader The classloader for resolving class references and
* primary key classes.
*/
private Collection<MappedSuperclass> mapClass2x(final Mapping mapping, final EntityBean bean, final ClassLoader classLoader) {
final Set<String> allFields = new TreeSet<>();
// get an acculated set of the CMP fields.
for (final CmpField cmpField : bean.getCmpField()) {
allFields.add(cmpField.getFieldName());
}
Class<?> beanClass = null;
try {
beanClass = classLoader.loadClass(bean.getEjbClass());
} catch (final ClassNotFoundException e) {
// if it does fail, just return null from here
return null;
}
// build a map from the field name to the super class that contains that field.
// If this is a strictly CMP 2.x class, this is generally an empty map. However,
// we support some migration steps toward EJB3, so this can be defined completely
// or partially as a POJO with concrete fields and accessors. This allows us to
// locate and generate the mappings
final Map<String, MappedSuperclass> superclassByField = mapFields(beanClass, allFields);
for (final Method method : beanClass.getMethods()) {
if (!Modifier.isAbstract(method.getModifiers())) {
continue;
}
if (method.getParameterTypes().length != 0) {
continue;
}
if (method.getReturnType().equals(Void.TYPE)) {
continue;
}
// Skip relationships: anything of type EJBLocalObject or Collection
if (EJBLocalObject.class.isAssignableFrom(method.getReturnType())) {
continue;
}
if (Collection.class.isAssignableFrom(method.getReturnType())) {
continue;
}
if (Map.class.isAssignableFrom(method.getReturnType())) {
continue;
}
String name = method.getName();
if (name.startsWith("get")) {
name = name.substring("get".length(), name.length());
} else if (name.startsWith("is")) {
// boolean.
if (method.getReturnType() == Boolean.TYPE) {
name = name.substring("is".length(), name.length());
} else {
// not an acceptable "is" method.
continue;
}
} else {
continue;
}
// the property needs the first character lowercased. Generally,
// we'll have this field already in our list, but it might have been
// omitted from the meta data.
name = Strings.lcfirst(name);
if (!allFields.contains(name)) {
allFields.add(name);
bean.addCmpField(name);
}
}
//
// id: the primary key
//
final Set<String> primaryKeyFields = new HashSet<>();
if (bean.getPrimkeyField() != null) {
final String fieldName = bean.getPrimkeyField();
final MappedSuperclass superclass = superclassByField.get(fieldName);
// this need not be here...for CMP 2.x, these are generally autogenerated fields.
if (superclass != null) {
// ok, add this field to the superclass mapping
superclass.addField(new Id(fieldName));
// the direct mapping is an over ride
mapping.addField(new AttributeOverride(fieldName));
} else {
// this is a normal generated field, it will be in the main class mapping.
mapping.addField(new Id(fieldName));
}
primaryKeyFields.add(fieldName);
} else if ("java.lang.Object".equals(bean.getPrimKeyClass())) {
// the automatically generated keys use a special property name
// and will always be in the generated superclass.
final String fieldName = "OpenEJB_pk";
final Id field = new Id(fieldName);
field.setGeneratedValue(new GeneratedValue(GenerationType.AUTO));
mapping.addField(field);
primaryKeyFields.add(fieldName);
} else if (bean.getPrimKeyClass() != null) {
Class<?> pkClass = null;
try {
pkClass = classLoader.loadClass(bean.getPrimKeyClass());
MappedSuperclass idclass = null;
// to make sure everything maps correctly.
for (final Field pkField : pkClass.getFields()) {
final String pkFieldName = pkField.getName();
final int modifiers = pkField.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && allFields.contains(pkFieldName)) {
// see if the bean field is concretely defined in one of the superclasses
final MappedSuperclass superclass = superclassByField.get(pkFieldName);
if (superclass != null) {
// ok, we have an override that needs to be specified at the main class level.
superclass.addField(new Id(pkFieldName));
mapping.addField(new AttributeOverride(pkFieldName));
idclass = resolveIdClass(idclass, superclass, beanClass);
} else {
// this field will be autogenerated
mapping.addField(new Id(pkFieldName));
}
primaryKeyFields.add(pkFieldName);
}
}
// if we've located an ID class, set it as such
if (idclass != null) {
idclass.setIdClass(new IdClass(bean.getPrimKeyClass()));
} else {
// do this for the toplevel mapping
mapping.setIdClass(new IdClass(bean.getPrimKeyClass()));
}
} catch (final ClassNotFoundException e) {
throw (IllegalStateException) new IllegalStateException("Could not find entity primary key class " + bean.getPrimKeyClass()).initCause(e);
}
}
//
for (final CmpField cmpField : bean.getCmpField()) {
// only add entries for cmp fields that are not part of the primary key
if (!primaryKeyFields.contains(cmpField.getFieldName())) {
final String fieldName = cmpField.getFieldName();
// this will be here if we've already processed this
final MappedSuperclass superclass = superclassByField.get(fieldName);
// we need to provide a mapping for this.
if (superclass != null) {
// we need to mark this as being in one of the superclasses
superclass.addField(new Basic(fieldName));
mapping.addField(new AttributeOverride(fieldName));
} else {
// directly generated.
mapping.addField(new Basic(fieldName));
}
}
}
// the field mappings
return new HashSet<>(superclassByField.values());
}
use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.
the class CmpJpaConversion method mapFields.
/**
* Build a mapping between a bean's CMP fields and the
* particular subclass in the inheritance hierarchy that
* defines the field.
*
* @param clazz The bean implementation class.
* @param persistantFields The set of container-managed fields.
* @return A map of fieldname-to-defining class relationships.
*/
private Map<String, MappedSuperclass> mapFields(Class clazz, Set<String> persistantFields) {
persistantFields = new TreeSet<>(persistantFields);
final Map<String, MappedSuperclass> fields = new TreeMap<>();
// or we've reached the Object class.
while (!persistantFields.isEmpty() && !clazz.equals(Object.class)) {
// This is a single target for the relationship mapping for each
// class in the hierarchy.
final MappedSuperclass superclass = new MappedSuperclass(clazz.getName());
for (final Field field : clazz.getDeclaredFields()) {
if (!field.isSynthetic()) {
final String fieldName = field.getName();
// if this is one of bean's persistence fields, create the mapping
if (persistantFields.contains(fieldName)) {
fields.put(fieldName, superclass);
persistantFields.remove(fieldName);
} else if (!ENHANCED_FIELDS.contains(fieldName)) {
// these are fields we need to identify as transient for the persistence engine.
final Transient transientField = new Transient(fieldName);
superclass.addField(transientField);
}
}
}
clazz = clazz.getSuperclass();
}
return fields;
}
use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.
the class CmpJpaConversion method removeMappedSuperclass.
/**
* Check the user-defined entity mappings for a superclass
* mapping for a given named class. If the user mappings exist,
* remove them from the user list and return them
* for inclusion in our generated mappings.
*
* @param userMappings The current user mapping set.
* @param className The name of the class of interest.
* @return Returns the superclass mapping for the named class, or
* null if the class is not in the mapping set.
*/
private MappedSuperclass removeMappedSuperclass(final EntityMappings userMappings, final String className) {
final MappedSuperclass mappedSuperclass;
mappedSuperclass = userMappings.getMappedSuperclassMap().get(className);
if (mappedSuperclass != null) {
userMappings.getMappedSuperclassMap().remove(mappedSuperclass.getKey());
}
return mappedSuperclass;
}
use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.
the class CmpJpaConversion method deploy.
public AppModule deploy(final AppModule appModule) throws OpenEJBException {
if (!hasCmpEntities(appModule)) {
return appModule;
}
// create mappings if no mappings currently exist
EntityMappings cmpMappings = appModule.getCmpMappings();
if (cmpMappings == null) {
cmpMappings = new EntityMappings();
cmpMappings.setVersion("1.0");
appModule.setCmpMappings(cmpMappings);
}
final Set<String> definedMappedClasses = new HashSet<>();
// check for an existing "cmp" persistence unit, and look at existing mappings
final PersistenceUnit cmpPersistenceUnit = findCmpPersistenceUnit(appModule);
if (cmpPersistenceUnit != null) {
if (cmpPersistenceUnit.getMappingFile() != null && cmpPersistenceUnit.getMappingFile().size() > 0) {
for (final String mappingFile : cmpPersistenceUnit.getMappingFile()) {
final EntityMappings entityMappings = readEntityMappings(mappingFile, appModule);
if (entityMappings != null) {
definedMappedClasses.addAll(entityMappings.getEntityMap().keySet());
}
}
}
}
// app mapping data
for (final EjbModule ejbModule : appModule.getEjbModules()) {
final EjbJar ejbJar = ejbModule.getEjbJar();
// scan for CMP entity beans and merge the data into the collective set
for (final EnterpriseBean enterpriseBean : ejbJar.getEnterpriseBeans()) {
if (isCmpEntity(enterpriseBean)) {
processEntityBean(ejbModule, definedMappedClasses, cmpMappings, (EntityBean) enterpriseBean);
}
}
// if there are relationships defined in this jar, get a list of the defined
// entities and process the relationship maps.
final Relationships relationships = ejbJar.getRelationships();
if (relationships != null) {
final Map<String, Entity> entitiesByEjbName = new TreeMap<>();
for (final Entity entity : cmpMappings.getEntity()) {
entitiesByEjbName.put(entity.getEjbName(), entity);
}
for (final EjbRelation relation : relationships.getEjbRelation()) {
processRelationship(entitiesByEjbName, relation);
}
}
// Let's warn the user about any declarations we didn't end up using
// so there can be no misunderstandings.
final EntityMappings userMappings = getUserEntityMappings(ejbModule);
for (final Entity mapping : userMappings.getEntity()) {
LOGGER.warning("openejb-cmp-orm.xml mapping ignored: module=" + ejbModule.getModuleId() + ": <entity class=\"" + mapping.getClazz() + "\">");
}
for (final MappedSuperclass mapping : userMappings.getMappedSuperclass()) {
LOGGER.warning("openejb-cmp-orm.xml mapping ignored: module=" + ejbModule.getModuleId() + ": <mapped-superclass class=\"" + mapping.getClazz() + "\">");
}
}
if (!cmpMappings.getEntity().isEmpty()) {
final PersistenceUnit persistenceUnit = getCmpPersistenceUnit(appModule);
final boolean generatedOrmXmlProvided = appModule.getClassLoader().getResource(GENERATED_ORM_XML) != null;
if (!persistenceUnit.getMappingFile().contains(GENERATED_ORM_XML)) {
// explicit check for openejb-cmp-generated-orm, as this is generated and added to <mapping-file>
if (generatedOrmXmlProvided) {
LOGGER.warning("App module " + appModule.getModuleId() + " provides " + GENERATED_ORM_XML + ", but does not " + "specify it using <mapping-file> in persistence.xml for the CMP persistence unit, and it may conflict " + "with the generated mapping file. Consider renaming the file and explicitly referencing it in persistence.xml");
}
persistenceUnit.getMappingFile().add(GENERATED_ORM_XML);
} else {
if (generatedOrmXmlProvided) {
LOGGER.warning("App module " + appModule.getModuleId() + " provides " + GENERATED_ORM_XML + " and additionally " + cmpMappings.getEntity().size() + "mappings have been generated. Consider renaming the " + GENERATED_ORM_XML + " in " + "your deployment archive to avoid any conflicts.");
}
}
for (final Entity entity : cmpMappings.getEntity()) {
if (!persistenceUnit.getClazz().contains(entity.getClazz())) {
persistenceUnit.getClazz().add(entity.getClazz());
}
}
}
// causes some of the unit tests to fail. Not sure why. Should be fixed.
for (final Entity entity : appModule.getCmpMappings().getEntity()) {
if (entity.getAttributes() != null && entity.getAttributes().isEmpty()) {
entity.setAttributes(null);
}
}
return appModule;
}
Aggregations