com.impetus.kundera.configure.MetamodelConfiguration Maven / Gradle / Ivy
/*******************************************************************************
* * Copyright 2012 Impetus Infotech.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
******************************************************************************/
package com.impetus.kundera.configure;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Table;
import javax.persistence.metamodel.Metamodel;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.impetus.kundera.PersistenceProperties;
import com.impetus.kundera.classreading.ClasspathReader;
import com.impetus.kundera.classreading.Reader;
import com.impetus.kundera.classreading.ResourceIterator;
import com.impetus.kundera.loader.MetamodelLoaderException;
import com.impetus.kundera.metadata.KunderaMetadataManager;
import com.impetus.kundera.metadata.MetadataBuilder;
import com.impetus.kundera.metadata.model.ApplicationMetadata;
import com.impetus.kundera.metadata.model.EntityMetadata;
import com.impetus.kundera.metadata.model.IdDiscriptor;
import com.impetus.kundera.metadata.model.KunderaMetadata;
import com.impetus.kundera.metadata.model.MetamodelImpl;
import com.impetus.kundera.metadata.model.PersistenceUnitMetadata;
import com.impetus.kundera.metadata.processor.GeneratedValueProcessor;
import com.impetus.kundera.metadata.validator.EntityValidator;
import com.impetus.kundera.metadata.validator.EntityValidatorImpl;
import com.impetus.kundera.utils.KunderaCoreUtils;
/**
* The Metamodel configurer: a) Configure application meta data b) loads entity
* metadata and maps metadata.
*
* @author vivek.mishra
*/
public class MetamodelConfiguration extends AbstractSchemaConfiguration implements Configuration
{
/** The log. */
private static Logger log = LoggerFactory.getLogger(MetamodelConfiguration.class);
/**
* Constructor using persistence units as parameter.
*
* @param persistenceUnits
* persistence units.
*/
public MetamodelConfiguration(Map properties, String... persistenceUnits)
{
super(persistenceUnits, properties);
}
/*
* (non-Javadoc)
*
* @see com.impetus.kundera.configure.Configuration#configure()
*/
@Override
public void configure()
{
log.debug("Loading Entity Metadata...");
ApplicationMetadata appMetadata = KunderaMetadata.INSTANCE.getApplicationMetadata();
for (String persistenceUnit : persistenceUnits)
{
if (appMetadata.getMetamodelMap().get(persistenceUnit.trim()) != null)
{
log.debug("Metadata already exists for the Persistence Unit " + persistenceUnit + ". Nothing to do");
}
else
{
loadEntityMetadata(persistenceUnit);
}
}
}
/**
* Load entity metadata.
*
* @param persistenceUnit
* the persistence unit
*/
private void loadEntityMetadata(String persistenceUnit)
{
if (persistenceUnit == null)
{
throw new IllegalArgumentException(
"Must have a persistenceUnitName in order to load entity metadata, you provided:" + persistenceUnit);
}
KunderaMetadata kunderaMetadata = KunderaMetadata.INSTANCE;
Map persistentUnitMetadataMap = kunderaMetadata.getApplicationMetadata()
.getPersistenceUnitMetadataMap();
/** Classes to scan */
List classesToScan;
URL[] resources = null;
String client = null;
List managedURLs = null;
if (persistentUnitMetadataMap == null || persistentUnitMetadataMap.isEmpty())
{
log.error("It is necessary to load Persistence Unit metadata for persistence unit " + persistenceUnit
+ " first before loading entity metadata.");
throw new MetamodelLoaderException("load Persistence Unit metadata for persistence unit "
+ persistenceUnit + " first before loading entity metadata.");
}
else
{
PersistenceUnitMetadata puMetadata = persistentUnitMetadataMap.get(persistenceUnit);
classesToScan = puMetadata.getManagedClassNames();
managedURLs = puMetadata.getManagedURLs();
Map externalProperties = KunderaCoreUtils.getExternalProperties(persistenceUnit,
externalPropertyMap, persistenceUnits);
client = externalProperties != null ? (String) externalProperties
.get(PersistenceProperties.KUNDERA_CLIENT_FACTORY) : null;
if (client == null)
{
client = puMetadata.getClient();
}
}
/*
* Check whether Classes to scan was provided into persistence.xml If
* yes, load them. Otherwise load them from classpath/ context path
*/
Reader reader;
ApplicationMetadata appMetadata = kunderaMetadata.getApplicationMetadata();
if (classesToScan == null || classesToScan.isEmpty())
{
log.info("No class to scan for persistence unit " + persistenceUnit
+ ". Entities will be loaded from classpath/ context-path");
// Entity metadata is not related to any PU, and hence will be
// stored at common place
// persistenceUnit = Constants.COMMON_ENTITY_METADATAS;
// Check whether all common entity metadata have already been loaded
if (appMetadata.getMetamodelMap().get(persistenceUnit) != null)
{
log.info("All common entitity metadata already loaded, nothing need to be done");
return;
}
reader = new ClasspathReader();
// resources = reader.findResourcesByClasspath();
}
else
{
reader = new ClasspathReader(classesToScan);
// resources = reader.findResourcesByContextLoader();
}
InputStream[] iStreams = null;
if (this.getClass().getClassLoader() instanceof URLClassLoader)
{
URL[] managedClasses = reader.findResources();
if (managedClasses != null)
{
List managedResources = Arrays.asList(managedClasses);
managedURLs.addAll(managedResources);
}
}
else
{
iStreams = reader.findResourcesAsStream();
}
if (managedURLs != null)
{
resources = managedURLs.toArray(new URL[] {});
}
// All entities to load should be annotated with @Entity
reader.addValidAnnotations(Entity.class.getName());
Metamodel metamodel = appMetadata.getMetamodel(persistenceUnit);
if (metamodel == null)
{
metamodel = new MetamodelImpl();
}
Map entityMetadataMap = ((MetamodelImpl) metamodel).getEntityMetadataMap();
Map> entityNameToClassMap = ((MetamodelImpl) metamodel).getEntityNameToClassMap();
Map> puToClazzMap = new HashMap>();
Map entityNameToKeyDiscriptorMap = new HashMap();
List> classes = new ArrayList>();
if (resources != null && resources.length > 0)
{
for (URL resource : resources)
{
try
{
ResourceIterator itr = reader.getResourceIterator(resource, reader.getFilter());
InputStream is = null;
while ((is = itr.next()) != null)
{
classes.addAll(scanClassAndPutMetadata(is, reader, entityMetadataMap, entityNameToClassMap,
persistenceUnit, client, puToClazzMap, entityNameToKeyDiscriptorMap));
}
}
catch (IOException e)
{
log.error("Error while retreiving and storing entity metadata. Details:", e);
throw new MetamodelLoaderException("Error while retreiving and storing entity metadata");
}
}
}
else if (iStreams != null)
{
try
{
for (InputStream is : iStreams)
{
try
{
classes.addAll(scanClassAndPutMetadata(is, reader, entityMetadataMap, entityNameToClassMap,
persistenceUnit, client, puToClazzMap, entityNameToKeyDiscriptorMap));
}
finally
{
if (is != null)
{
is.close();
}
}
}
}
catch (IOException e)
{
log.error("Error while retreiving and storing entity metadata. Details:", e);
throw new MetamodelLoaderException("Error while retreiving and storing entity metadata, Caused by : .",
e);
}
}
((MetamodelImpl) metamodel).setEntityMetadataMap(entityMetadataMap);
appMetadata.getMetamodelMap().put(persistenceUnit, metamodel);
appMetadata.setClazzToPuMap(puToClazzMap);
((MetamodelImpl) metamodel).addKeyValues(entityNameToKeyDiscriptorMap);
// assign JPA metamodel.
((MetamodelImpl) metamodel).assignEmbeddables(KunderaMetadata.INSTANCE.getApplicationMetadata()
.getMetaModelBuilder(persistenceUnit).getEmbeddables());
((MetamodelImpl) metamodel).assignManagedTypes(KunderaMetadata.INSTANCE.getApplicationMetadata()
.getMetaModelBuilder(persistenceUnit).getManagedTypes());
((MetamodelImpl) metamodel).assignMappedSuperClass(KunderaMetadata.INSTANCE.getApplicationMetadata()
.getMetaModelBuilder(persistenceUnit).getMappedSuperClassTypes());
// validateEntityForClientSpecificProperty(classes, persistenceUnit);
}
/**
* @param resources
* @param reader
* @throws IOException
* @throws ClassNotFoundException
*/
private void validateEntityForClientSpecificProperty(List> classes, final String persistenceUnit)
{
for (Class clazz : classes)
{
String pu = getPersistenceUnitOfEntity(clazz);
EntityValidator validator = new EntityValidatorImpl(KunderaCoreUtils.getExternalProperties(persistenceUnit,
externalPropertyMap, persistenceUnits));
if (clazz.isAnnotationPresent(Entity.class) && clazz.isAnnotationPresent(Table.class)
&& persistenceUnit.equalsIgnoreCase(pu))
{
validator.validateEntity(clazz);
}
}
}
private String getPersistenceUnitOfEntity(Class clazz)
{
String schema = ((Table) clazz.getAnnotation(Table.class)).schema();
String pu = null;
if (schema != null && schema.indexOf("@") > 0)
{
pu = schema.substring(schema.indexOf("@") + 1, schema.length());
}
return pu;
}
/**
* Scan class and put metadata.
*
* @param bits
* the bits
* @param reader
* the reader
* @param entityMetadataMap
* the entity metadata map
* @param entityNameToClassMap
* the entity name to class map
* @param keyDiscriptor
* @param persistence
* unit the persistence unit.
* @throws IOException
* Signals that an I/O exception has occurred.
*/
private List> scanClassAndPutMetadata(InputStream bits, Reader reader,
Map entityMetadataMap, Map> entityNameToClassMap,
String persistenceUnit, String client, Map> clazzToPuMap,
Map entityNameToKeyDiscriptorMap) throws IOException
{
DataInputStream dstream = new DataInputStream(new BufferedInputStream(bits));
ClassFile cf = null;
String className = null;
List> classes = new ArrayList>();
try
{
cf = new ClassFile(dstream);
className = cf.getName();
List annotations = new ArrayList();
reader.accumulateAnnotations(annotations,
(AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag));
reader.accumulateAnnotations(annotations,
(AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag));
// iterate through all valid annotations
for (String validAnn : reader.getValidAnnotations())
{
// check if the current class has one?
if (annotations.contains(validAnn))
{
// Class> clazz =
// Thread.currentThread().getContextClassLoader().loadClass(className);
Class> clazz = this.getClass().getClassLoader().loadClass(className);
//get the name of entity to be used for entity to class map if or not annotated with name
String entityName = getEntityName(clazz);
if ((entityNameToClassMap.containsKey(entityName)
&& !entityNameToClassMap.get(entityName).getName().equals(clazz.getName())))
{
throw new MetamodelLoaderException("Name conflict between classes "
+ entityNameToClassMap.get(entityName).getName() + " and " + clazz.getName()
+ ". Make sure no two entity classes with the same name "
+ " are specified for persistence unit " + persistenceUnit);
}
entityNameToClassMap.put(entityName, clazz);
EntityMetadata metadata = entityMetadataMap.get(clazz);
if (null == metadata)
{
log.debug("Metadata not found in cache for " + clazz.getName());
// double check locking.
synchronized (clazz)
{
if (null == metadata)
{
MetadataBuilder metadataBuilder = new MetadataBuilder(persistenceUnit, client,
KunderaCoreUtils.getExternalProperties(persistenceUnit, externalPropertyMap,
persistenceUnits));
metadata = metadataBuilder.buildEntityMetadata(clazz);
// in case entity's pu does not belong to parse
// persistence unit, it will be null.
if (metadata != null)
{
entityMetadataMap.put(clazz.getName(), metadata);
mapClazztoPu(clazz, persistenceUnit, clazzToPuMap);
processGeneratedValueAnnotation(clazz, persistenceUnit, metadata,
entityNameToKeyDiscriptorMap);
}
}
}
}
// TODO :
onValidateClientProperties(classes, clazz, persistenceUnit);
}
}
}
catch (ClassNotFoundException e)
{
log.error("Class " + className + " not found, it won't be loaded as entity");
}
finally
{
if (dstream != null)
{
dstream.close();
}
if (bits != null)
{
bits.close();
}
}
return classes;
}
/**
* @param clazz
*/
private String getEntityName(Class> clazz)
{
return !StringUtils.isBlank(clazz.getAnnotation(Entity.class).name()) ?
clazz.getAnnotation(Entity.class).name() : clazz.getSimpleName();
}
/**
* @param clazz
*/
private List> onValidateClientProperties(List> classes, Class> clazz,
final String persistenceUnit)
{
if (clazz.isAnnotationPresent(Entity.class) && clazz.isAnnotationPresent(Table.class))
{
classes.add(clazz);
}
return classes;
}
/**
* Method to prepare class simple name to list of pu's mapping. 1 class can
* be mapped to multiple persistence units, in case of RDBMS, in other cases
* it will only be 1!
*
* @param clazz
* entity class to be mapped.
* @param pu
* current persistence unit name
* @param clazzToPuMap
* collection holding mapping.
* @return map holding mapping.
*/
private Map> mapClazztoPu(Class> clazz, String pu, Map> clazzToPuMap)
{
List puCol = new ArrayList(1);
if (clazzToPuMap == null)
{
clazzToPuMap = new HashMap>();
}
else
{
if (clazzToPuMap.containsKey(clazz.getName()))
{
puCol = clazzToPuMap.get(clazz.getName());
}
}
if (!puCol.contains(pu))
{
puCol.add(pu);
clazzToPuMap.put(clazz.getName(), puCol);
String annotateEntityName = clazz.getAnnotation(Entity.class).name();
if (!StringUtils.isBlank(annotateEntityName))
{
clazzToPuMap.put(annotateEntityName, puCol);
}
}
return clazzToPuMap;
}
private void processGeneratedValueAnnotation(Class> clazz, String persistenceUnit, EntityMetadata m,
Map entityNameToKeyDiscriptorMap)
{
GeneratedValueProcessor processer = new GeneratedValueProcessor();
String pu = m.getPersistenceUnit()/* getPersistenceUnitOfEntity(clazz) */;
String clientFactoryName = KunderaMetadataManager.getPersistenceUnitMetadata(m.getPersistenceUnit())
.getClient();
if (pu != null && pu.equals(persistenceUnit)
|| clientFactoryName.equalsIgnoreCase("com.impetus.client.rdbms.RDBMSClientFactory"))
{
Field f = (Field) m.getIdAttribute().getJavaMember();
if (f.isAnnotationPresent(GeneratedValue.class))
{
processer.process(clazz, f, m, entityNameToKeyDiscriptorMap);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy